搜索替换后正文
粗体当前章节搜索结果
跳转搜索结果对应页面
pull/374/head
Jason Yao 4 years ago
parent 435c63f732
commit 04088a5dc1
  1. 22
      app/src/main/java/io/legado/app/service/help/ReadBook.kt
  2. 33
      app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt
  3. 8
      app/src/main/java/io/legado/app/ui/book/searchContent/SearchListAdapter.kt
  4. 57
      app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt
  5. 22
      app/src/main/java/io/legado/app/ui/book/searchContent/SearchResult.kt

@ -326,9 +326,21 @@ object ReadBook {
} }
} }
fun searchResultPositions(pages: List<TextPage>, contentPosition: Int): Array<Int>{ fun searchResultPositions(pages: List<TextPage>, indexWithinChapter: Int, query: String): Array<Int>{
// //
// calculate search result's pageIndex // calculate search result's pageIndex
var content = ""
pages.map{
content+= it.text
}
var count = 1
var index = content.indexOf(query)
while(count != indexWithinChapter){
index = content.indexOf(query, index + 1);
count += 1
}
Log.d("h11128", "new index $index")
val contentPosition = index
var pageIndex = 0 var pageIndex = 0
var length = pages[pageIndex].text.length var length = pages[pageIndex].text.length
Log.d("h11128", "page size ${pages.size}") Log.d("h11128", "page size ${pages.size}")
@ -348,6 +360,7 @@ object ReadBook {
val currentPage = pages[pageIndex] val currentPage = pages[pageIndex]
var lineIndex = 0 var lineIndex = 0
length = length - currentPage.text.length + currentPage.textLines[lineIndex].text.length length = length - currentPage.text.length + currentPage.textLines[lineIndex].text.length
Log.d("h11128", "currentLine ${currentPage.textLines[lineIndex].text}")
while (length < contentPosition){ while (length < contentPosition){
lineIndex += 1 lineIndex += 1
if (lineIndex >currentPage.textLines.size){ if (lineIndex >currentPage.textLines.size){
@ -355,14 +368,17 @@ object ReadBook {
break break
} }
length += currentPage.textLines[lineIndex].text.length length += currentPage.textLines[lineIndex].text.length
Log.d("h11128", "currentLine ${currentPage.textLines[lineIndex].text}")
} }
// charIndex // charIndex
Log.d("h11128", "currentLine ${currentPage.textLines[lineIndex].text}")
Log.d("h11128", "currentLength $length")
val currentLine = currentPage.textLines[lineIndex] val currentLine = currentPage.textLines[lineIndex]
length -= currentLine.text.length length -= currentLine.text.length
Log.d("h11128", "currentLength $length")
val charIndex = contentPosition - length val charIndex = contentPosition - length
Log.d("h11128", "contentLength $contentPosition")
return arrayOf(pageIndex, lineIndex, charIndex) return arrayOf(pageIndex, lineIndex, charIndex)
} }

@ -60,6 +60,9 @@ import kotlinx.android.synthetic.main.activity_book_read.*
import kotlinx.android.synthetic.main.view_read_menu.* import kotlinx.android.synthetic.main.view_read_menu.*
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.jetbrains.anko.sdk27.listeners.onClick import org.jetbrains.anko.sdk27.listeners.onClick
import org.jetbrains.anko.startActivity import org.jetbrains.anko.startActivity
@ -100,6 +103,7 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
override var isAutoPage = false override var isAutoPage = false
private var screenTimeOut: Long = 0 private var screenTimeOut: Long = 0
private var timeBatteryReceiver: TimeBatteryReceiver? = null private var timeBatteryReceiver: TimeBatteryReceiver? = null
private var loadStates: Boolean = false
override val pageFactory: TextPageFactory get() = page_view.pageFactory override val pageFactory: TextPageFactory get() = page_view.pageFactory
override val headerHeight: Int get() = page_view.curPage.headerHeight override val headerHeight: Int get() = page_view.curPage.headerHeight
@ -536,6 +540,7 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
intent.removeExtra("readAloud") intent.removeExtra("readAloud")
ReadBook.readAloud() ReadBook.readAloud()
} }
loadStates = true
} }
/** /**
@ -547,6 +552,7 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
page_view.upContent(relativePosition, resetPageOffset) page_view.upContent(relativePosition, resetPageOffset)
seek_read_page.progress = ReadBook.durPageIndex seek_read_page.progress = ReadBook.durPageIndex
} }
loadStates = false
} }
/** /**
@ -770,20 +776,29 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
} }
requestCodeSearchResult -> requestCodeSearchResult ->
data?.getIntExtra("index", ReadBook.durChapterIndex)?.let { index -> data?.getIntExtra("index", ReadBook.durChapterIndex)?.let { index ->
val contentPosition = data.getIntExtra("contentPosition", 0) launch(IO){
val indexWithinChapter = data.getIntExtra("indexWithinChapter", 0)
val query = data.getStringExtra("query") val query = data.getStringExtra("query")
ReadBook.durChapterIndex = index viewModel.openChapter(index)
ReadBook.saveRead()
ReadBook.loadContent(resetPageOffset = true) // block until load correct chapter and pages
while (ReadBook.durChapterIndex != index || ReadBook.curTextChapter?.pages == null ){
delay(100L)
}
val pages = ReadBook.curTextChapter?.pages val pages = ReadBook.curTextChapter?.pages
if (pages != null){ Log.d("Jason", "Current chapter pages ${pages!!.size}")
val positions = ReadBook.searchResultPositions(pages, contentPosition) val positions = ReadBook.searchResultPositions(pages, indexWithinChapter, query!!)
viewModel.openChapter(index, positions[0])
Log.d("h11128", positions[0].toString()) Log.d("h11128", positions[0].toString())
Log.d("h11128", positions[1].toString()) Log.d("h11128", positions[1].toString())
Log.d("h11128", positions[2].toString()) Log.d("h11128", positions[2].toString())
//page_view.curPage.selectStartMoveIndex(positions[0], positions[1], 0) //todo: show selected text
//page_view.curPage.selectEndMoveIndex(positions[0], positions[1], 0 + query!!.length ) val job1 = async(Main){
ReadBook.skipToPage(positions[0])
page_view.curPage.selectStartMoveIndex(positions[0], positions[1], 0)
page_view.curPage.selectEndMoveIndex(positions[0], positions[1], 0 + query.length )
page_view.isTextSelected = true
}
job1.await()
} }
} }
requestCodeReplace -> onReplaceRuleSave() requestCodeReplace -> onReplaceRuleSave()

@ -15,6 +15,7 @@ import io.legado.app.help.BookHelp
import io.legado.app.lib.theme.accentColor import io.legado.app.lib.theme.accentColor
import io.legado.app.utils.getCompatColor import io.legado.app.utils.getCompatColor
import io.legado.app.utils.visible import io.legado.app.utils.visible
import kotlinx.android.synthetic.main.item_bookmark.view.*
import kotlinx.android.synthetic.main.item_search_list.view.* import kotlinx.android.synthetic.main.item_search_list.view.*
import org.jetbrains.anko.sdk27.listeners.onClick import org.jetbrains.anko.sdk27.listeners.onClick
@ -25,8 +26,12 @@ class SearchListAdapter(context: Context, val callback: Callback) :
override fun convert(holder: ItemViewHolder, item: SearchResult, payloads: MutableList<Any>) { override fun convert(holder: ItemViewHolder, item: SearchResult, payloads: MutableList<Any>) {
with(holder.itemView) { with(holder.itemView) {
val isDur = callback.durChapterIndex() == item.chapterIndex
if (payloads.isEmpty()) { if (payloads.isEmpty()) {
tv_search_result.text = HtmlCompat.fromHtml(item.presentText, HtmlCompat.FROM_HTML_MODE_LEGACY) tv_search_result.text = item.parseText(item.presentText)
if (isDur){
tv_search_result.paint.isFakeBoldText = true
}
} }
} }
} }
@ -41,5 +46,6 @@ class SearchListAdapter(context: Context, val callback: Callback) :
interface Callback { interface Callback {
fun openSearchResult(searchResult: SearchResult) fun openSearchResult(searchResult: SearchResult)
fun durChapterIndex(): Int
} }
} }

@ -7,12 +7,14 @@ import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.View import android.view.View
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import com.hankcs.hanlp.HanLP
import io.legado.app.App import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.VMBaseFragment import io.legado.app.base.VMBaseFragment
import io.legado.app.constant.EventBus import io.legado.app.constant.EventBus
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookChapter
import io.legado.app.help.AppConfig
import io.legado.app.help.BookHelp import io.legado.app.help.BookHelp
import io.legado.app.lib.theme.bottomBackground import io.legado.app.lib.theme.bottomBackground
import io.legado.app.lib.theme.getPrimaryTextColor import io.legado.app.lib.theme.getPrimaryTextColor
@ -40,7 +42,7 @@ class SearchListFragment : VMBaseFragment<SearchListViewModel>(R.layout.fragment
lateinit var adapter: SearchListAdapter lateinit var adapter: SearchListAdapter
private lateinit var mLayoutManager: UpLinearLayoutManager private lateinit var mLayoutManager: UpLinearLayoutManager
private var searchResultCounts = 0 private var searchResultCounts = 0
private var pageSize = 0 private var durChapterIndex = 0
private var searchResultList: MutableList<SearchResult> = mutableListOf() private var searchResultList: MutableList<SearchResult> = mutableListOf()
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
@ -76,9 +78,11 @@ class SearchListFragment : VMBaseFragment<SearchListViewModel>(R.layout.fragment
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
private fun initBook() { private fun initBook() {
launch { launch {
tv_current_search_info.text = "搜索结果:$searchResultCounts" tv_current_search_info.text = "搜索结果:$searchResultCounts"
viewModel.book?.let { viewModel.book?.let {
initCacheFileNames(it) initCacheFileNames(it)
durChapterIndex = it.durChapterIndex
} }
} }
} }
@ -117,15 +121,15 @@ class SearchListFragment : VMBaseFragment<SearchListViewModel>(R.layout.fragment
val job = async(IO){ val job = async(IO){
if (isLocalBook || adapter.cacheFileNames.contains(BookHelp.formatChapterName(chapter))) { if (isLocalBook || adapter.cacheFileNames.contains(BookHelp.formatChapterName(chapter))) {
searchResults = searchChapter(newText, chapter) searchResults = searchChapter(newText, chapter)
Log.d("h11128", "find ${searchResults.size} results in chapter ${chapter.title}") //Log.d("h11128", "find ${searchResults.size} results in chapter ${chapter.title}")
} }
} }
job.await() job.await()
if (searchResults.isNotEmpty()){ if (searchResults.isNotEmpty()){
Log.d("h11128", "load ${searchResults.size} results in chapter ${chapter.title}") //Log.d("h11128", "load ${searchResults.size} results in chapter ${chapter.title}")
searchResultList.addAll(searchResults) searchResultList.addAll(searchResults)
tv_current_search_info.text = "搜索结果:$searchResultCounts" tv_current_search_info.text = "搜索结果:$searchResultCounts"
Log.d("h11128", "searchResultList size ${searchResultList.size}") //Log.d("h11128", "searchResultList size ${searchResultList.size}")
adapter.addItems(searchResults) adapter.addItems(searchResults)
searchResults = listOf<SearchResult>() searchResults = listOf<SearchResult>()
} }
@ -134,55 +138,51 @@ class SearchListFragment : VMBaseFragment<SearchListViewModel>(R.layout.fragment
} }
} }
private fun searchChapter(query: String, chapter: BookChapter?): List<SearchResult> { private suspend fun searchChapter(query: String, chapter: BookChapter?): List<SearchResult> {
val searchResults: MutableList<SearchResult> = mutableListOf() val searchResults: MutableList<SearchResult> = mutableListOf()
var positions : List<Int>? = listOf() var positions : List<Int> = listOf()
var replaceContents: List<String>? = null
var totalContents = ""
if (chapter != null){ if (chapter != null){
viewModel.book?.let { bookSource -> viewModel.book?.let { bookSource ->
val bookContent = BookHelp.getContent(bookSource, chapter) val bookContent = BookHelp.getContent(bookSource, chapter)
if (bookContent != null){ if (bookContent != null){
//todo: 搜索替换后的正文句子列表 //搜索替换后的正文
/* val job = async(IO) {
chapter.title = when (AppConfig.chineseConverterType) { chapter.title = when (AppConfig.chineseConverterType) {
1 -> HanLP.convertToSimplifiedChinese(chapter.title) 1 -> HanLP.convertToSimplifiedChinese(chapter.title)
2 -> HanLP.convertToTraditionalChinese(chapter.title) 2 -> HanLP.convertToTraditionalChinese(chapter.title)
else -> chapter.title else -> chapter.title
} }
var replaceContents: List<String>? = null
bookContent?.let {
replaceContents = BookHelp.disposeContent( replaceContents = BookHelp.disposeContent(
chapter.title, chapter.title,
bookSource.name, bookSource.name,
bookSource.bookUrl, bookSource.bookUrl,
it, bookContent,
bookSource.useReplaceRule bookSource.useReplaceRule
) )
} }
job.await()
replaceContents?.map { while (replaceContents == null){
async(IO) { delay(100L)
if (it.contains(query)) { }
Log.d("targetd contents", it) totalContents = replaceContents!!.joinToString("")
searchResult.add(it) positions = searchPosition(totalContents, query)
} var count = 1
} positions.map{
}?.awaitAll() val construct = constructText(totalContents, it, query)
*/
positions = searchPosition(bookContent, query)
positions?.map{
val construct = constructText(bookContent, it, query)
val result = SearchResult(index = searchResultCounts, val result = SearchResult(index = searchResultCounts,
indexWithinChapter = count,
text = construct[1] as String, text = construct[1] as String,
chapterTitle = chapter.title, chapterTitle = chapter.title,
query = query, query = query,
pageIndex = 0, //todo: 计算搜索结果所在的pageIndex直接跳转
chapterIndex = chapter.index, chapterIndex = chapter.index,
newPosition = construct[0] as Int, newPosition = construct[0] as Int,
contentPosition = it contentPosition = it
) )
count += 1
searchResultCounts += 1 searchResultCounts += 1
searchResults.add(result) searchResults.add(result)
// Log.d("h11128", result.presentText)
} }
} }
} }
@ -228,6 +228,7 @@ class SearchListFragment : VMBaseFragment<SearchListViewModel>(R.layout.fragment
searchData.putExtra("index", searchResult.chapterIndex) searchData.putExtra("index", searchResult.chapterIndex)
searchData.putExtra("contentPosition", searchResult.contentPosition) searchData.putExtra("contentPosition", searchResult.contentPosition)
searchData.putExtra("query", searchResult.query) searchData.putExtra("query", searchResult.query)
searchData.putExtra("indexWithinChapter", searchResult.indexWithinChapter)
Log.d("h11128","current chapter index ${searchResult.chapterIndex}") Log.d("h11128","current chapter index ${searchResult.chapterIndex}")
activity?.setResult(RESULT_OK, searchData) activity?.setResult(RESULT_OK, searchData)
activity?.finish() activity?.finish()
@ -235,4 +236,8 @@ class SearchListFragment : VMBaseFragment<SearchListViewModel>(R.layout.fragment
} }
override fun durChapterIndex(): Int {
return durChapterIndex
}
} }

@ -1,10 +1,13 @@
package io.legado.app.ui.book.searchContent package io.legado.app.ui.book.searchContent
import android.text.Spanned
import android.util.Log import android.util.Log
import androidx.core.text.HtmlCompat
import io.legado.app.ui.book.read.page.entities.TextPage import io.legado.app.ui.book.read.page.entities.TextPage
data class SearchResult( data class SearchResult(
var index: Int = 0, var index: Int = 0,
var indexWithinChapter: Int = 0,
var text: String = "", var text: String = "",
var chapterTitle: String = "", var chapterTitle: String = "",
val query: String, val query: String,
@ -16,13 +19,22 @@ data class SearchResult(
) { ) {
val presentText: String val presentText: String
get(){ get(){
val sub1 = text.substring(0, newPosition) return colorPresentText(newPosition, query, text) +
val sub2 = text.substring(newPosition + query.length, text.length)
return "<font color=#000000>$sub1</font>" +
"<font color=#ff0000>$query</font>" +
"<font color=#000000>$sub2</font>" +
"<font color=#0000ff>($chapterTitle)</font>" "<font color=#0000ff>($chapterTitle)</font>"
} }
fun colorPresentText(position: Int, center: String, targetText: String): String{
val sub1 = text.substring(0, position)
val sub2 = text.substring(position + center.length, targetText.length)
return "<font color=#000000>$sub1</font>" +
"<font color=#ff0000>$center</font>" +
"<font color=#000000>$sub2</font>"
}
fun parseText(targetText: String): Spanned {
return HtmlCompat.fromHtml(targetText, HtmlCompat.FROM_HTML_MODE_LEGACY)
}
} }
Loading…
Cancel
Save