add and delete

add data class searchList
delete durchapterindex
pull/374/head
Jason Yao 4 years ago
parent 1e94325f80
commit ec9e554d0d
  1. 3
      app/src/main/java/io/legado/app/ui/book/searchContent/SearchListActivity.kt
  2. 37
      app/src/main/java/io/legado/app/ui/book/searchContent/SearchListAdapter.kt
  3. 116
      app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt
  4. 22
      app/src/main/java/io/legado/app/ui/book/searchContent/SearchResult.kt
  5. 16
      app/src/main/res/layout/item_search_list.xml

@ -45,6 +45,7 @@ class SearchListActivity : VMBaseActivity<SearchListViewModel>(R.layout.activity
searchView?.onActionViewCollapsed() searchView?.onActionViewCollapsed()
searchView?.setOnCloseListener { searchView?.setOnCloseListener {
tab_layout.visible() tab_layout.visible()
//to do clean
false false
} }
searchView?.setOnSearchClickListener { tab_layout.gone() } searchView?.setOnSearchClickListener { tab_layout.gone() }
@ -62,7 +63,7 @@ class SearchListActivity : VMBaseActivity<SearchListViewModel>(R.layout.activity
return super.onCompatCreateOptionsMenu(menu) return super.onCompatCreateOptionsMenu(menu)
} }
private inner class TabFragmentPageAdapter internal constructor(fm: FragmentManager) : private inner class TabFragmentPageAdapter(fm: FragmentManager) :
FragmentPagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { FragmentPagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
override fun getItem(position: Int): Fragment { override fun getItem(position: Int): Fragment {
return SearchListFragment() return SearchListFragment()

@ -1,7 +1,11 @@
package io.legado.app.ui.book.searchContent package io.legado.app.ui.book.searchContent
import android.content.Context import android.content.Context
import android.os.Build
import android.text.Html
import android.view.View import android.view.View
import androidx.annotation.RequiresApi
import androidx.core.text.HtmlCompat
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.adapter.ItemViewHolder import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.SimpleRecyclerAdapter import io.legado.app.base.adapter.SimpleRecyclerAdapter
@ -10,32 +14,23 @@ 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.tv_chapter_name import kotlinx.android.synthetic.main.item_search_list.view.*
import kotlinx.android.synthetic.main.item_chapter_list.view.*
import org.jetbrains.anko.sdk27.listeners.onClick import org.jetbrains.anko.sdk27.listeners.onClick
class SearchListAdapter(context: Context, val callback: Callback) : class SearchListAdapter(context: Context, val callback: Callback) :
SimpleRecyclerAdapter<BookChapter>(context, R.layout.item_search_list) { SimpleRecyclerAdapter<SearchResult>(context, R.layout.item_search_list) {
val cacheFileNames = hashSetOf<String>() val cacheFileNames = hashSetOf<String>()
override fun convert(holder: ItemViewHolder, item: BookChapter, payloads: MutableList<Any>) { @RequiresApi(Build.VERSION_CODES.N)
override fun convert(holder: ItemViewHolder, item: SearchResult, payloads: MutableList<Any>) {
with(holder.itemView) { with(holder.itemView) {
val isDur = callback.durChapterIndex() == item.index
val cached = callback.isLocalBook
|| cacheFileNames.contains(BookHelp.formatChapterName(item))
if (payloads.isEmpty()) { if (payloads.isEmpty()) {
// set search result color here // set search result color here
if (isDur) { tv_search_result.text = HtmlCompat.fromHtml(item.presentText, HtmlCompat.FROM_HTML_MODE_LEGACY)
tv_chapter_name.setTextColor(context.accentColor)
} else { } else {
tv_chapter_name.setTextColor(context.getCompatColor(R.color.primaryText)) //to do
}
tv_chapter_name.text = item.title
upHasCache(this, isDur, cached)
} else {
upHasCache(this, isDur, cached)
} }
} }
} }
@ -49,18 +44,10 @@ class SearchListAdapter(context: Context, val callback: Callback) :
} }
private fun upHasCache(itemView: View, isDur: Boolean, cached: Boolean) = itemView.apply { private fun upHasCache(itemView: View, isDur: Boolean, cached: Boolean) = itemView.apply {
tv_chapter_name.paint.isFakeBoldText = cached tv_search_result.paint.isFakeBoldText = cached
iv_checked.setImageResource(R.drawable.ic_outline_cloud_24)
iv_checked.visible(!cached)
if (isDur) {
iv_checked.setImageResource(R.drawable.ic_check)
iv_checked.visible()
}
} }
interface Callback { interface Callback {
val isLocalBook: Boolean fun openSearchResult(searchResult: SearchResult)
fun openSearchResult(bookChapter: BookChapter)
fun durChapterIndex(): Int
} }
} }

@ -32,10 +32,9 @@ class SearchListFragment : VMBaseFragment<SearchListViewModel>(R.layout.fragment
get() = getViewModelOfActivity(SearchListViewModel::class.java) get() = getViewModelOfActivity(SearchListViewModel::class.java)
lateinit var adapter: SearchListAdapter lateinit var adapter: SearchListAdapter
private var durChapterIndex = 0
private lateinit var mLayoutManager: UpLinearLayoutManager private lateinit var mLayoutManager: UpLinearLayoutManager
private var tocLiveData: LiveData<List<BookChapter>>? = null private var tocLiveData: LiveData<List<BookChapter>>? = null
private var scrollToDurChapter = false private var searchResultCounts = 0
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
viewModel.searchCallBack = this viewModel.searchCallBack = this
@ -69,35 +68,28 @@ class SearchListFragment : VMBaseFragment<SearchListViewModel>(R.layout.fragment
mLayoutManager.scrollToPositionWithOffset(adapter.itemCount - 1, 0) mLayoutManager.scrollToPositionWithOffset(adapter.itemCount - 1, 0)
} }
} }
tv_current_search_info.onClick {
mLayoutManager.scrollToPositionWithOffset(durChapterIndex, 0)
}
} }
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
private fun initBook() { private fun initBook() {
launch { launch {
initDoc()
viewModel.book?.let { viewModel.book?.let {
durChapterIndex = it.durChapterIndex
tv_current_search_info.text = tv_current_search_info.text =
"${it.durChapterTitle}(${it.durChapterIndex + 1}/${it.totalChapterNum})" "搜索结果:$searchResultCounts"
initCacheFileNames(it) initCacheFileNames(it)
} }
} }
} }
/*
private fun initDoc() { private fun initDoc() {
tocLiveData?.removeObservers(this@SearchListFragment) tocLiveData?.removeObservers(this@SearchListFragment)
tocLiveData = App.db.bookChapterDao().observeByBook(viewModel.bookUrl) tocLiveData = App.db.bookChapterDao().observeByBook(viewModel.bookUrl)
tocLiveData?.observe(viewLifecycleOwner, { tocLiveData?.observe(viewLifecycleOwner, {
adapter.setItems(it) adapter.setItems(it)
if (!scrollToDurChapter) {
mLayoutManager.scrollToPositionWithOffset(durChapterIndex, 0)
scrollToDurChapter = true
}
}) })
} }
*/
private fun initCacheFileNames(book: Book) { private fun initCacheFileNames(book: Book) {
launch(IO) { launch(IO) {
@ -120,46 +112,34 @@ class SearchListFragment : VMBaseFragment<SearchListViewModel>(R.layout.fragment
} }
override fun startContentSearch(newText: String?) { override fun startContentSearch(newText: String?) {
if (newText.isNullOrBlank()) { if (!newText.isNullOrBlank()) {
initDoc()
} else {
var count: Int = 0
val beginTime = System.currentTimeMillis()
App.db.bookChapterDao().getChapterList(viewModel.bookUrl).map{ App.db.bookChapterDao().getChapterList(viewModel.bookUrl).map{
launch(IO) { launch(IO) {
val beginTime = System.currentTimeMillis()
if (isLocalBook || if (isLocalBook ||
adapter.cacheFileNames.contains(BookHelp.formatChapterName(it)) adapter.cacheFileNames.contains(BookHelp.formatChapterName(it))
) { ) {
val value = searchChapter(newText, it) val value = searchChapter(newText, it)
count += value searchResultCounts += value
}
} }
}
//adapter.setItems(list)
val finishedTime = System.currentTimeMillis() - beginTime val finishedTime = System.currentTimeMillis() - beginTime
Log.d("Jason", "Search finished, the total time cost is $finishedTime") Log.d("Jason", "Search finished, the total time cost is $finishedTime")
Log.d("Jason", "Search finished, the total count is $count") Log.d("Jason", "Search finished, the total count is $searchResultCounts")
//tocLiveData?.removeObservers(this) }
//tocLiveData = App.db.bookChapterDao().liveDataSearch(viewModel.bookUrl, newText)
//tocLiveData?.observe(viewLifecycleOwner, {
//adapter.setItems(it)
//})
} }
} }
}
private suspend fun searchChapter(query: String, chapter: BookChapter?): Int { private suspend fun searchChapter(query: String, chapter: BookChapter?): Int {
val startTime = System.currentTimeMillis() val startTime = System.currentTimeMillis()
val searchResult: MutableList<String> = mutableListOf() val searchResult: MutableList<SearchResult> = mutableListOf()
var count = 0 var positions : List<Int>? = listOf()
if (chapter != null){ if (chapter != null){
Log.d("Jason", "Search ${chapter.title}") Log.d("Jason", "Search ${chapter.title}")
viewModel.book?.let { bookSource -> viewModel.book?.let { bookSource ->
val bookContent = BookHelp.getContent(bookSource, chapter) val bookContent = BookHelp.getContent(bookSource, chapter)
if (bookContent != null){
/* replace content, let's focus on original content first /* replace content, let's focus on original content first
chapter.title = when (AppConfig.chineseConverterType) { chapter.title = when (AppConfig.chineseConverterType) {
1 -> HanLP.convertToSimplifiedChinese(chapter.title) 1 -> HanLP.convertToSimplifiedChinese(chapter.title)
@ -186,35 +166,73 @@ class SearchListFragment : VMBaseFragment<SearchListViewModel>(R.layout.fragment
} }
}?.awaitAll() }?.awaitAll()
*/ */
count = bookContent?.let { countMatches(it, query) }!! positions = countMatches(bookContent, query)
Log.d("Jason", "Search ${chapter.title} finished, the appeared count is $count") positions?.map{
searchResult.add(
SearchResult(index = 0,
text = constructText(bookContent, it),
chapterTitle = chapter.title,
query = query,
pageSize = 0, // to be finished
chapterIndex = chapter.index, // to be finished
pageIndex = 0, // to be finished
)
)
}
adapter.addItems(searchResult)
Log.d("Jason", "Search ${chapter.title} finished, the appeared count is ${positions!!.size}")
}
} }
val endTime = System.currentTimeMillis() - startTime val endTime = System.currentTimeMillis() - startTime
Log.d("Jason", "Search ${chapter.title} finished, the time cost is $endTime") Log.d("Jason", "Search ${chapter.title} finished, the time cost is $endTime")
} }
return count return positions!!.size
} }
fun countMatches(string: String, pattern: String): Int { private fun countMatches(content: String, pattern: String): List<Int> {
val matcher = Pattern.compile(pattern).matcher(string) val position : MutableList<Int> = mutableListOf()
var count = 0 var count = 0
while (matcher.find()) { var index = content.indexOf(pattern)
count++ while(index >= 0){
position.add(index)
index = content.indexOf(pattern, index + 1);
} }
return count return position
} }
private fun constructText(content: String, position: Int): String{
override val isLocalBook: Boolean val length = 10
get() = viewModel.book?.isLocalBook() == true var po1 = position - length
var po2 = position + length
override fun durChapterIndex(): Int { if (po1 <0) {
return durChapterIndex po1 = 0
}
if (po2 > content.length){
po2 = content.length
} }
return content.substring(po1, po2)
/*
if (position >= length && position <= content.length - length){
return content.substring(position - length, position + length)
}
else if (position <= length){
return content.substring(0, position + length)
}
else if (position >= content.length - length){
return content.substring(position - length, content.length)
}
*/
}
val isLocalBook: Boolean
get() = viewModel.book?.isLocalBook() == true
override fun openSearchResult(bookChapter: BookChapter) { override fun openSearchResult(searchResult: SearchResult) {
activity?.setResult(RESULT_OK, Intent().putExtra("index", bookChapter.index)) val searchData = Intent()
searchData.putExtra("index", searchResult.chapterIndex)
searchData.putExtra("pageIndex", searchResult.pageIndex)
activity?.setResult(RESULT_OK, searchData)
activity?.finish() activity?.finish()
} }

@ -0,0 +1,22 @@
package io.legado.app.ui.book.searchContent
data class SearchResult(
var index: Int = 0,
var text: String = "",
var chapterTitle: String = "",
val query: String,
var pageSize: Int = 0,
var chapterIndex: Int = 0,
var pageIndex: Int = 0,
) {
val presentText: String
get(){
val sub1 = text.substring(0, text.indexOf(query))
val sub2 = text.substring(text.indexOf(query) + 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>"
}
}

@ -8,24 +8,12 @@
android:padding="12dp"> android:padding="12dp">
<TextView <TextView
android:id="@+id/tv_chapter_name" android:id="@+id/tv_search_result"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:singleLine="true" android:singleLine="true"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/iv_checked" /> app:layout_constraintRight_toLeftOf="parent" />
<ImageView
android:id="@+id/iv_checked"
android:layout_width="16dp"
android:layout_height="16dp"
android:src="@drawable/ic_check"
android:visibility="invisible"
android:contentDescription="@string/success"
app:tint="@color/secondaryText"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
Loading…
Cancel
Save