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