Просмотр исходного кода

Merge pull request #2072 from 821938089/fix-content-search-bug

优化全文搜索
kunfei 4 лет назад
Родитель
Сommit
0670566113

+ 25 - 10
app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt

@@ -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)
-                ReadBook.skipToPage(positions[0]) {
+                val (pageIndex, lineIndex, charIndex, addLine, charIndex2) =
+                    viewModel.searchResultPositions(it, searchResult)
+                ReadBook.skipToPage(pageIndex) {
                     launch {
                     launch {
                         isSelectingSearchResult = true
                         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 -> binding.readView.curPage.selectEndMoveIndex(
                                 0,
                                 0,
-                                positions[1],
-                                positions[2] + viewModel.searchContentQuery.length - 1
+                                lineIndex,
+                                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() {

+ 20 - 18
app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt

@@ -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
-        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
             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
         // 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)
     }
     }

+ 17 - 5
app/src/main/java/io/legado/app/ui/book/read/SearchMenu.kt

@@ -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)
     }
     }
 
 
 }
 }

+ 2 - 1
app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt

@@ -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
                 }
                 }
             }
             }
         }
         }

+ 2 - 2
app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt

@@ -294,8 +294,8 @@ class PageView(context: Context) : FrameLayout(context) {
         binding.contentTextView.selectEndMoveIndex(relativePagePos, lineIndex, charIndex)
         binding.contentTextView.selectEndMoveIndex(relativePagePos, lineIndex, charIndex)
     }
     }
 
 
-    fun cancelSelect() {
-        binding.contentTextView.cancelSelect()
+    fun cancelSelect(fromSearchExit: Boolean = false) {
+        binding.contentTextView.cancelSelect(fromSearchExit)
     }
     }
 
 
     fun createBookmark(): Bookmark? {
     fun createBookmark(): Bookmark? {

+ 16 - 6
app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentActivity.kt

@@ -56,9 +56,18 @@ class SearchContentActivity :
         initSearchView()
         initSearchView()
         initRecyclerView()
         initRecyclerView()
         initView()
         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")
     @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()
     }
     }

+ 2 - 2
app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentAdapter.kt

@@ -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
     }
     }
 }
 }

+ 1 - 1
app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentViewModel.kt

@@ -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

+ 7 - 4
app/src/main/java/io/legado/app/ui/book/searchContent/SearchResult.kt

@@ -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) +
-                    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)
             HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_LEGACY)
         } else {
         } else {
             HtmlCompat.fromHtml(
             HtmlCompat.fromHtml(