优化全文搜索
This commit is contained in:
@@ -113,13 +113,15 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
it ?: return@registerForActivityResult
|
||||
it.data?.let { data ->
|
||||
val key = data.getLongExtra("key", System.currentTimeMillis())
|
||||
val index = data.getIntExtra("index", 0)
|
||||
val searchResult = IntentData.get<SearchResult>("searchResult$key")
|
||||
val searchResultList = IntentData.get<List<SearchResult>>("searchResultList$key")
|
||||
if (searchResult != null && searchResultList != null) {
|
||||
viewModel.searchContentQuery = searchResult.query
|
||||
binding.searchMenu.upSearchResultList(searchResultList)
|
||||
isShowingSearchResult = true
|
||||
binding.searchMenu.updateSearchResultIndex(searchResultList.indexOf(searchResult))
|
||||
viewModel.searchResultIndex = index
|
||||
binding.searchMenu.updateSearchResultIndex(index)
|
||||
binding.searchMenu.selectedSearchResult?.let { currentResult ->
|
||||
skipToSearch(currentResult)
|
||||
showActionMenu()
|
||||
@@ -888,6 +890,12 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
searchContentActivity.launch(Intent(this, SearchContentActivity::class.java).apply {
|
||||
putExtra("bookUrl", it.bookUrl)
|
||||
putExtra("searchWord", searchWord ?: viewModel.searchContentQuery)
|
||||
putExtra("searchResultIndex", viewModel.searchResultIndex)
|
||||
viewModel.searchResultList?.first()?.let {
|
||||
if (it.query == viewModel.searchContentQuery) {
|
||||
IntentData.put("searchResultList", viewModel.searchResultList)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -930,6 +938,8 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
isShowingSearchResult = false
|
||||
binding.searchMenu.invalidate()
|
||||
binding.searchMenu.invisible()
|
||||
binding.readView.isTextSelected = false
|
||||
binding.readView.curPage.cancelSelect(true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1052,7 +1062,8 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
}
|
||||
}
|
||||
|
||||
override fun navigateToSearch(searchResult: SearchResult) {
|
||||
override fun navigateToSearch(searchResult: SearchResult, index: Int) {
|
||||
viewModel.searchResultIndex = index
|
||||
skipToSearch(searchResult)
|
||||
}
|
||||
|
||||
@@ -1062,22 +1073,23 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
fun jumpToPosition() {
|
||||
ReadBook.curTextChapter?.let {
|
||||
binding.searchMenu.updateSearchInfo()
|
||||
val positions = viewModel.searchResultPositions(it, searchResult)
|
||||
ReadBook.skipToPage(positions[0]) {
|
||||
val (pageIndex, lineIndex, charIndex, addLine, charIndex2) =
|
||||
viewModel.searchResultPositions(it, searchResult)
|
||||
ReadBook.skipToPage(pageIndex) {
|
||||
launch {
|
||||
isSelectingSearchResult = true
|
||||
binding.readView.curPage.selectStartMoveIndex(0, positions[1], positions[2])
|
||||
when (positions[3]) {
|
||||
binding.readView.curPage.selectStartMoveIndex(0, lineIndex, charIndex)
|
||||
when (addLine) {
|
||||
0 -> binding.readView.curPage.selectEndMoveIndex(
|
||||
0,
|
||||
positions[1],
|
||||
positions[2] + viewModel.searchContentQuery.length - 1
|
||||
lineIndex,
|
||||
charIndex + viewModel.searchContentQuery.length - 1
|
||||
)
|
||||
1 -> binding.readView.curPage.selectEndMoveIndex(
|
||||
0, positions[1] + 1, positions[4]
|
||||
0, lineIndex + 1, charIndex2
|
||||
)
|
||||
//consider change page, jump to scroll position
|
||||
-1 -> binding.readView.curPage.selectEndMoveIndex(1, 0, positions[4])
|
||||
-1 -> binding.readView.curPage.selectEndMoveIndex(1, 0, charIndex2)
|
||||
}
|
||||
binding.readView.isTextSelected = true
|
||||
isSelectingSearchResult = false
|
||||
@@ -1196,6 +1208,9 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
observeEvent<String>(PreferKey.showBrightnessView) {
|
||||
readMenu.upBrightnessState()
|
||||
}
|
||||
observeEvent<List<SearchResult>>(EventBus.SEARCH_RESULT) {
|
||||
viewModel.searchResultList = it
|
||||
}
|
||||
}
|
||||
|
||||
private fun upScreenTimeOut() {
|
||||
|
||||
@@ -34,7 +34,6 @@ import io.legado.app.utils.toStringArray
|
||||
import io.legado.app.utils.toastOnUi
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
/**
|
||||
* 阅读界面数据处理
|
||||
@@ -43,6 +42,8 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
|
||||
val permissionDenialLiveData = MutableLiveData<Int>()
|
||||
var isInitFinish = false
|
||||
var searchContentQuery = ""
|
||||
var searchResultList: List<SearchResult>? = null
|
||||
var searchResultIndex: Int = 0
|
||||
private var changeSourceCoroutine: Coroutine<*>? = null
|
||||
|
||||
/**
|
||||
@@ -360,53 +361,54 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
|
||||
// calculate search result's pageIndex
|
||||
val pages = textChapter.pages
|
||||
val content = textChapter.getContent()
|
||||
val queryLength = searchContentQuery.length
|
||||
|
||||
var count = 0
|
||||
var index = content.indexOf(searchContentQuery)
|
||||
while (count != searchResult.resultCountWithinChapter) {
|
||||
index = content.indexOf(searchContentQuery, index + 1)
|
||||
index = content.indexOf(searchContentQuery, index + queryLength)
|
||||
count += 1
|
||||
}
|
||||
val contentPosition = index
|
||||
var pageIndex = 0
|
||||
var length = pages[pageIndex].text.length
|
||||
while (length < contentPosition) {
|
||||
while (length < contentPosition && pageIndex + 1 < pages.size) {
|
||||
pageIndex += 1
|
||||
if (pageIndex > pages.size) {
|
||||
pageIndex = pages.size
|
||||
break
|
||||
}
|
||||
length += pages[pageIndex].text.length
|
||||
}
|
||||
|
||||
// calculate search result's lineIndex
|
||||
val currentPage = pages[pageIndex]
|
||||
val curTextLines = currentPage.textLines
|
||||
var lineIndex = 0
|
||||
length = length - currentPage.text.length + currentPage.textLines[lineIndex].text.length
|
||||
while (length < contentPosition) {
|
||||
var curLine = curTextLines[lineIndex]
|
||||
length = length - currentPage.text.length + curLine.text.length
|
||||
if (curLine.isParagraphEnd) length++
|
||||
while (length < contentPosition && lineIndex + 1 < curTextLines.size) {
|
||||
lineIndex += 1
|
||||
if (lineIndex > currentPage.textLines.size) {
|
||||
lineIndex = currentPage.textLines.size
|
||||
break
|
||||
}
|
||||
length += currentPage.textLines[lineIndex].text.length
|
||||
curLine = curTextLines[lineIndex]
|
||||
length += curLine.text.length
|
||||
if (curLine.isParagraphEnd) length++
|
||||
}
|
||||
|
||||
// charIndex
|
||||
val currentLine = currentPage.textLines[lineIndex]
|
||||
length -= currentLine.text.length
|
||||
var curLineLength = currentLine.text.length
|
||||
if (currentLine.isParagraphEnd) curLineLength++
|
||||
length -= curLineLength
|
||||
|
||||
val charIndex = contentPosition - length
|
||||
var addLine = 0
|
||||
var charIndex2 = 0
|
||||
// change line
|
||||
if ((charIndex + searchContentQuery.length) > currentLine.text.length) {
|
||||
if ((charIndex + queryLength) > curLineLength) {
|
||||
addLine = 1
|
||||
charIndex2 = charIndex + searchContentQuery.length - currentLine.text.length - 1
|
||||
charIndex2 = charIndex + queryLength - curLineLength - 1
|
||||
}
|
||||
// changePage
|
||||
if ((lineIndex + addLine + 1) > currentPage.textLines.size) {
|
||||
addLine = -1
|
||||
charIndex2 = charIndex + searchContentQuery.length - currentLine.text.length - 1
|
||||
charIndex2 = charIndex + queryLength - curLineLength - 1
|
||||
}
|
||||
return arrayOf(pageIndex, lineIndex, charIndex, addLine, charIndex2)
|
||||
}
|
||||
|
||||
@@ -148,22 +148,34 @@ class SearchMenu @JvmOverloads constructor(
|
||||
|
||||
fabLeft.setOnClickListener {
|
||||
updateSearchResultIndex(currentSearchResultIndex - 1)
|
||||
callBack.navigateToSearch(searchResultList[currentSearchResultIndex])
|
||||
callBack.navigateToSearch(
|
||||
searchResultList[currentSearchResultIndex],
|
||||
currentSearchResultIndex
|
||||
)
|
||||
}
|
||||
|
||||
ivSearchContentUp.setOnClickListener {
|
||||
updateSearchResultIndex(currentSearchResultIndex - 1)
|
||||
callBack.navigateToSearch(searchResultList[currentSearchResultIndex])
|
||||
callBack.navigateToSearch(
|
||||
searchResultList[currentSearchResultIndex],
|
||||
currentSearchResultIndex
|
||||
)
|
||||
}
|
||||
|
||||
ivSearchContentDown.setOnClickListener {
|
||||
updateSearchResultIndex(currentSearchResultIndex + 1)
|
||||
callBack.navigateToSearch(searchResultList[currentSearchResultIndex])
|
||||
callBack.navigateToSearch(
|
||||
searchResultList[currentSearchResultIndex],
|
||||
currentSearchResultIndex
|
||||
)
|
||||
}
|
||||
|
||||
fabRight.setOnClickListener {
|
||||
updateSearchResultIndex(currentSearchResultIndex + 1)
|
||||
callBack.navigateToSearch(searchResultList[currentSearchResultIndex])
|
||||
callBack.navigateToSearch(
|
||||
searchResultList[currentSearchResultIndex],
|
||||
currentSearchResultIndex
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,7 +237,7 @@ class SearchMenu @JvmOverloads constructor(
|
||||
fun upSystemUiVisibility()
|
||||
fun exitSearchMenu()
|
||||
fun showMenuBar()
|
||||
fun navigateToSearch(searchResult: SearchResult)
|
||||
fun navigateToSearch(searchResult: SearchResult, index: Int)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -397,12 +397,13 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
||||
upSelectedEnd(x, y + headerHeight)
|
||||
}
|
||||
|
||||
fun cancelSelect() {
|
||||
fun cancelSelect(fromSearchExit: Boolean = false) {
|
||||
val last = if (callBack.isScroll) 2 else 0
|
||||
for (relativePos in 0..last) {
|
||||
relativePage(relativePos).textLines.forEach { textLine ->
|
||||
textLine.textChars.forEach {
|
||||
it.selected = false
|
||||
if (fromSearchExit) it.isSearchResult = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,8 +294,8 @@ class PageView(context: Context) : FrameLayout(context) {
|
||||
binding.contentTextView.selectEndMoveIndex(relativePagePos, lineIndex, charIndex)
|
||||
}
|
||||
|
||||
fun cancelSelect() {
|
||||
binding.contentTextView.cancelSelect()
|
||||
fun cancelSelect(fromSearchExit: Boolean = false) {
|
||||
binding.contentTextView.cancelSelect(fromSearchExit)
|
||||
}
|
||||
|
||||
fun createBookmark(): Bookmark? {
|
||||
|
||||
@@ -56,9 +56,18 @@ class SearchContentActivity :
|
||||
initSearchView()
|
||||
initRecyclerView()
|
||||
initView()
|
||||
intent.getStringExtra("bookUrl")?.let {
|
||||
viewModel.initBook(it) {
|
||||
initBook()
|
||||
val searchResultList = IntentData.get<List<SearchResult>>("searchResultList")
|
||||
val submit = searchResultList == null
|
||||
intent.getStringExtra("bookUrl")?.let { bookUrl ->
|
||||
viewModel.initBook(bookUrl) {
|
||||
searchResultList?.let {
|
||||
viewModel.searchResultList.addAll(it)
|
||||
viewModel.searchResultCounts = it.size
|
||||
adapter.setItems(it)
|
||||
val position = intent.getIntExtra("searchResultIndex", 0)
|
||||
binding.recyclerView.scrollToPosition(position)
|
||||
}
|
||||
initBook(submit)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,14 +110,14 @@ class SearchContentActivity :
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun initBook() {
|
||||
private fun initBook(submit: Boolean = true) {
|
||||
binding.tvCurrentSearchInfo.text =
|
||||
this.getString(R.string.search_content_size) + ": ${viewModel.searchResultCounts}"
|
||||
viewModel.book?.let {
|
||||
initCacheFileNames(it)
|
||||
durChapterIndex = it.durChapterIndex
|
||||
intent.getStringExtra("searchWord")?.let { searchWord ->
|
||||
searchView.setQuery(searchWord, true)
|
||||
searchView.setQuery(searchWord, submit)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -182,13 +191,14 @@ class SearchContentActivity :
|
||||
val isLocalBook: Boolean
|
||||
get() = viewModel.book?.isLocalBook() == true
|
||||
|
||||
override fun openSearchResult(searchResult: SearchResult) {
|
||||
override fun openSearchResult(searchResult: SearchResult, index: Int) {
|
||||
postEvent(EventBus.SEARCH_RESULT, viewModel.searchResultList as List<SearchResult>)
|
||||
val searchData = Intent()
|
||||
val key = System.currentTimeMillis()
|
||||
IntentData.put("searchResult$key", searchResult)
|
||||
IntentData.put("searchResultList$key", viewModel.searchResultList)
|
||||
searchData.putExtra("key", key)
|
||||
searchData.putExtra("index", index)
|
||||
setResult(RESULT_OK, searchData)
|
||||
finish()
|
||||
}
|
||||
|
||||
@@ -39,13 +39,13 @@ class SearchContentAdapter(context: Context, val callback: Callback) :
|
||||
override fun registerListener(holder: ItemViewHolder, binding: ItemSearchListBinding) {
|
||||
holder.itemView.setOnClickListener {
|
||||
getItem(holder.layoutPosition)?.let {
|
||||
if (it.query.isNotBlank()) callback.openSearchResult(it)
|
||||
if (it.query.isNotBlank()) callback.openSearchResult(it, holder.layoutPosition)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface Callback {
|
||||
fun openSearchResult(searchResult: SearchResult)
|
||||
fun openSearchResult(searchResult: SearchResult, index: Int)
|
||||
fun durChapterIndex(): Int
|
||||
}
|
||||
}
|
||||
@@ -97,7 +97,7 @@ class SearchContentViewModel(application: Application) : BaseViewModel(applicati
|
||||
while (index >= 0) {
|
||||
scope.ensureActive()
|
||||
position.add(index)
|
||||
index = mContent.indexOf(pattern, index + 1)
|
||||
index = mContent.indexOf(pattern, index + pattern.length)
|
||||
}
|
||||
}
|
||||
return position
|
||||
|
||||
@@ -22,10 +22,13 @@ data class SearchResult(
|
||||
val leftString = resultText.substring(0, queryIndexInSurrounding)
|
||||
val rightString =
|
||||
resultText.substring(queryIndexInSurrounding + query.length, resultText.length)
|
||||
val html = leftString.colorTextForHtml(textColor) +
|
||||
query.colorTextForHtml(accentColor) +
|
||||
rightString.colorTextForHtml(textColor) +
|
||||
chapterTitle.colorTextForHtml(accentColor)
|
||||
val html = buildString {
|
||||
append(chapterTitle.colorTextForHtml(accentColor))
|
||||
append("<br>")
|
||||
append(leftString.colorTextForHtml(textColor))
|
||||
append(query.colorTextForHtml(accentColor))
|
||||
append(rightString.colorTextForHtml(textColor))
|
||||
}
|
||||
HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_LEGACY)
|
||||
} else {
|
||||
HtmlCompat.fromHtml(
|
||||
|
||||
Reference in New Issue
Block a user