From 09b864e1b8c38462b6a76f1d7cda7b4e427576b5 Mon Sep 17 00:00:00 2001 From: kunfei Date: Fri, 22 Oct 2021 10:55:52 +0800 Subject: [PATCH] =?UTF-8?q?Revert=20"=E6=90=9C=E7=B4=A2=E8=8F=9C=E5=8D=95"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/ui/book/read/ReadBookActivity.kt | 18 -- .../io/legado/app/ui/book/read/SearchMenu.kt | 204 ------------- .../searchContent/SearchContentActivity.kt | 115 +++++++- .../searchContent/SearchContentAdapter.kt | 1 + .../searchContent/SearchContentViewModel.kt | 79 +---- .../app/ui/book/searchContent/SearchResult.kt | 41 +-- .../main/res/layout/activity_book_read.xml | 6 - app/src/main/res/layout/view_search_menu.xml | 276 ------------------ 8 files changed, 126 insertions(+), 614 deletions(-) delete mode 100644 app/src/main/java/io/legado/app/ui/book/read/SearchMenu.kt delete mode 100644 app/src/main/res/layout/view_search_menu.xml diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt index 16e5dce9d..c566d6aab 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt @@ -65,7 +65,6 @@ class ReadBookActivity : BaseReadBookActivity(), TextActionMenu.CallBack, ContentTextView.CallBack, ReadMenu.CallBack, - SearchMenu.CallBack, ReadAloudDialog.CallBack, ChangeSourceDialog.CallBack, ReadBook.CallBack, @@ -118,7 +117,6 @@ class ReadBookActivity : BaseReadBookActivity(), private var backupJob: Job? = null override var autoPageProgress = 0 override var isAutoPage = false - override var isShowingSearchResult = true private var screenTimeOut: Long = 0 private var timeBatteryReceiver: TimeBatteryReceiver? = null private var loadStates: Boolean = false @@ -637,7 +635,6 @@ class ReadBookActivity : BaseReadBookActivity(), when { BaseReadAloudService.isRun -> showReadAloudDialog() isAutoPage -> showDialogFragment() - isShowingSearchResult -> binding.searchMenu.runMenuIn() else -> binding.readMenu.runMenuIn() } } @@ -769,14 +766,6 @@ class ReadBookActivity : BaseReadBookActivity(), showDialogFragment() } - override fun returnSearchActivity() { - TODO("Not yet implemented") - } - - override fun showSearchSetting() { - TODO("Not yet implemented") - } - /** * 更新状态栏,导航栏 */ @@ -785,13 +774,6 @@ class ReadBookActivity : BaseReadBookActivity(), upNavigationBarColor() } - override fun searchExit() { - if(isShowingSearchResult){ - isShowingSearchResult = false - binding.searchMenu.invalidate() - } - } - override fun showLogin() { ReadBook.bookSource?.let { startActivity { diff --git a/app/src/main/java/io/legado/app/ui/book/read/SearchMenu.kt b/app/src/main/java/io/legado/app/ui/book/read/SearchMenu.kt deleted file mode 100644 index 8341062ce..000000000 --- a/app/src/main/java/io/legado/app/ui/book/read/SearchMenu.kt +++ /dev/null @@ -1,204 +0,0 @@ -package io.legado.app.ui.book.read - -import android.annotation.SuppressLint -import android.content.Context -import android.content.res.ColorStateList -import android.graphics.drawable.GradientDrawable -import android.util.AttributeSet -import android.view.Gravity -import android.view.LayoutInflater -import android.view.View.OnClickListener -import android.view.View.OnLongClickListener -import android.view.WindowManager -import android.view.animation.Animation -import android.widget.FrameLayout -import android.widget.SeekBar -import androidx.activity.viewModels -import androidx.appcompat.widget.SearchView -import androidx.core.view.isVisible -import io.legado.app.R -import io.legado.app.constant.PreferKey -import io.legado.app.databinding.ViewSearchMenuBinding -import io.legado.app.help.* -import io.legado.app.lib.dialogs.alert -import io.legado.app.lib.theme.* -import io.legado.app.model.ReadBook -import io.legado.app.ui.book.info.BookInfoActivity -import io.legado.app.ui.book.searchContent.SearchContentViewModel -import io.legado.app.ui.browser.WebViewActivity -import io.legado.app.ui.widget.seekbar.SeekBarChangeListener -import io.legado.app.utils.* -import splitties.views.* - -/** - * 阅读界面菜单 - */ -class SearchMenu @JvmOverloads constructor( - context: Context, attrs: AttributeSet? = null -) : FrameLayout(context, attrs) { - private val searchView: SearchView by lazy { - binding.titleBar.findViewById(R.id.search_view) - } - - val viewModel by viewModels() - - private val callBack: CallBack get() = activity as CallBack - private val binding = ViewSearchMenuBinding.inflate(LayoutInflater.from(context), this, true) - private val menuTopIn: Animation = AnimationUtilsSupport.loadAnimation(context, R.anim.anim_readbook_top_in) - private val menuTopOut: Animation = AnimationUtilsSupport.loadAnimation(context, R.anim.anim_readbook_top_out) - private val menuBottomIn: Animation = AnimationUtilsSupport.loadAnimation(context, R.anim.anim_readbook_bottom_in) - private val menuBottomOut: Animation = AnimationUtilsSupport.loadAnimation(context, R.anim.anim_readbook_bottom_out) - private val bgColor: Int = context.bottomBackground - private val textColor: Int = context.getPrimaryTextColor(ColorUtils.isColorLight(bgColor)) - private val bottomBackgroundList: ColorStateList = - Selector.colorBuild().setDefaultColor(bgColor).setPressedColor(ColorUtils.darkenColor(bgColor)).create() - private var onMenuOutEnd: (() -> Unit)? = null - private var hasSearchResult: Boolean = true - - init { - initAnimation() - initView() - bindEvent() - } - - private fun initView() = binding.run { - llSearchBaseInfo.setBackgroundColor(bgColor) - tvCurrentSearchInfo.setTextColor(bottomBackgroundList) - llBottomBg.setBackgroundColor(bgColor) - fabLeft.backgroundTintList = bottomBackgroundList - fabLeft.setColorFilter(textColor) - fabRight.backgroundTintList = bottomBackgroundList - fabRight.setColorFilter(textColor) - tvMainMenu.setTextColor(textColor) - tvSearchResults.setTextColor(textColor) - tvSearchExit.setTextColor(textColor) - tvSetting.setTextColor(textColor) - ivMainMenu.setColorFilter(textColor) - ivSearchResults.setColorFilter(textColor) - ivSearchExit.setColorFilter(textColor) - ivSetting.setColorFilter(textColor) - ivSearchContentBottom.setColorFilter(textColor) - ivSearchContentTop.setColorFilter(textColor) - } - - - fun runMenuIn() { - this.visible() - binding.titleBar.visible() - binding.llSearchBaseInfo.visible() - binding.llBottomBg.visible() - binding.titleBar.startAnimation(menuTopIn) - binding.llSearchBaseInfo.startAnimation(menuBottomIn) - binding.llBottomBg.startAnimation(menuBottomIn) - } - - fun runMenuOut(onMenuOutEnd: (() -> Unit)? = null) { - this.onMenuOutEnd = onMenuOutEnd - if (this.isVisible) { - binding.titleBar.startAnimation(menuTopOut) - binding.llSearchBaseInfo.startAnimation(menuBottomOut) - binding.llBottomBg.startAnimation(menuBottomOut) - } - } - - private fun bindEvent() = binding.run { - titleBar.toolbar.setOnClickListener { - ReadBook.book?.let { - context.startActivity { - putExtra("name", it.name) - putExtra("author", it.author) - } - } - } - - llSearchResults.setOnClickListener { - runMenuOut { - callBack.returnSearchActivity() - } - } - - //主菜单 - llMainMenu.setOnClickListener { - runMenuOut { - callBack.showMenuBar() - } - } - - - //目录 - llSearchExit.setOnClickListener { - runMenuOut { - callBack.searchExit() - } - } - - //设置 - llSetting.setOnClickListener { - runMenuOut { - callBack.showSearchSetting() - } - } - } - - private fun initAnimation() { - //显示菜单 - menuTopIn.setAnimationListener(object : Animation.AnimationListener { - override fun onAnimationStart(animation: Animation) { - callBack.upSystemUiVisibility() - binding.fabLeft.visible(hasSearchResult) - binding.fabRight.visible(hasSearchResult) - } - - @SuppressLint("RtlHardcoded") - override fun onAnimationEnd(animation: Animation) { - val navigationBarHeight = if (ReadBookConfig.hideNavigationBar) { - activity?.navigationBarHeight ?: 0 - } else { - 0 - } - binding.run { - vwMenuBg.setOnClickListener { runMenuOut() } - root.padding = 0 - when (activity?.navigationBarGravity) { - Gravity.BOTTOM -> root.bottomPadding = navigationBarHeight - Gravity.LEFT -> root.leftPadding = navigationBarHeight - Gravity.RIGHT -> root.rightPadding = navigationBarHeight - } - } - callBack.upSystemUiVisibility() - } - - override fun onAnimationRepeat(animation: Animation) = Unit - }) - - //隐藏菜单 - menuTopOut.setAnimationListener(object : Animation.AnimationListener { - override fun onAnimationStart(animation: Animation) { - binding.vwMenuBg.setOnClickListener(null) - } - - override fun onAnimationEnd(animation: Animation) { - this@SearchMenu.invisible() - binding.titleBar.invisible() - binding.llSearchBaseInfo.invisible() - binding.llBottomBg.invisible() - binding.fabRight.invisible() - binding.fabLeft.invisible() - onMenuOutEnd?.invoke() - callBack.upSystemUiVisibility() - } - - override fun onAnimationRepeat(animation: Animation) = Unit - }) - } - - interface CallBack { - var isShowingSearchResult: Boolean - fun returnSearchActivity() - fun showSearchSetting() - fun upSystemUiVisibility() - fun searchExit() - fun showMenuBar() - } - -} diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentActivity.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentActivity.kt index dc074a29a..5bb46882e 100644 --- a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentActivity.kt @@ -5,6 +5,7 @@ import android.content.Intent import android.os.Bundle import androidx.activity.viewModels import androidx.appcompat.widget.SearchView +import com.github.liuyueyi.quick.transfer.ChineseUtils import io.legado.app.R import io.legado.app.base.VMBaseActivity import io.legado.app.constant.EventBus @@ -12,6 +13,7 @@ import io.legado.app.data.appDb import io.legado.app.data.entities.Book import io.legado.app.data.entities.BookChapter import io.legado.app.databinding.ActivitySearchContentBinding +import io.legado.app.help.AppConfig import io.legado.app.help.BookHelp import io.legado.app.lib.theme.bottomBackground import io.legado.app.lib.theme.getPrimaryTextColor @@ -38,7 +40,9 @@ class SearchContentActivity : private val searchView: SearchView by lazy { binding.titleBar.findViewById(R.id.search_view) } + private var searchResultCounts = 0 private var durChapterIndex = 0 + private var searchResultList: MutableList = mutableListOf() override fun onActivityCreated(savedInstanceState: Bundle?) { val bbg = bottomBackground @@ -99,7 +103,7 @@ class SearchContentActivity : @SuppressLint("SetTextI18n") private fun initBook() { - binding.tvCurrentSearchInfo.text = "搜索结果:${viewModel.searchResultCounts}" + binding.tvCurrentSearchInfo.text = "搜索结果:$searchResultCounts" viewModel.book?.let { initCacheFileNames(it) durChapterIndex = it.durChapterIndex @@ -111,7 +115,7 @@ class SearchContentActivity : private fun initCacheFileNames(book: Book) { launch(Dispatchers.IO) { - viewModel.cacheChapterNames.addAll(BookHelp.getChapterFiles(book)) + adapter.cacheFileNames.addAll(BookHelp.getChapterFiles(book)) withContext(Dispatchers.Main) { adapter.notifyItemRangeChanged(0, adapter.itemCount, true) } @@ -122,7 +126,7 @@ class SearchContentActivity : observeEvent(EventBus.SAVE_CONTENT) { chapter -> viewModel.book?.bookUrl?.let { bookUrl -> if (chapter.bookUrl == bookUrl) { - viewModel.cacheChapterNames.add(chapter.getFileName()) + adapter.cacheFileNames.add(chapter.getFileName()) adapter.notifyItemChanged(chapter.index, true) } } @@ -130,26 +134,28 @@ class SearchContentActivity : } @SuppressLint("SetTextI18n") - fun startContentSearch(query: String) { + fun startContentSearch(newText: String) { // 按章节搜索内容 - if (query.isNotBlank()) { + if (newText.isNotBlank()) { adapter.clearItems() - viewModel.searchResultList.clear() - viewModel.searchResultCounts = 0 - viewModel.lastQuery = query + searchResultList.clear() + binding.refreshProgressBar.isAutoLoading = true + searchResultCounts = 0 + viewModel.lastQuery = newText var searchResults = listOf() launch(Dispatchers.Main) { - appDb.bookChapterDao.getChapterList(viewModel.bookUrl).map { bookChapter -> - binding.refreshProgressBar.isAutoLoading = true + appDb.bookChapterDao.getChapterList(viewModel.bookUrl).map { chapter -> withContext(Dispatchers.IO) { - if (isLocalBook || viewModel.cacheChapterNames.contains(bookChapter.getFileName())) { - searchResults = viewModel.searchChapter(query, bookChapter) + if (isLocalBook + || adapter.cacheFileNames.contains(chapter.getFileName()) + ) { + searchResults = searchChapter(newText, chapter) } } if (searchResults.isNotEmpty()) { - viewModel.searchResultList.addAll(searchResults) + searchResultList.addAll(searchResults) binding.refreshProgressBar.isAutoLoading = false - binding.tvCurrentSearchInfo.text = "搜索结果:${viewModel.searchResultCounts}" + binding.tvCurrentSearchInfo.text = "搜索结果:$searchResultCounts" adapter.addItems(searchResults) searchResults = listOf() } @@ -158,6 +164,85 @@ class SearchContentActivity : } } + private suspend fun searchChapter(query: String, chapter: BookChapter?): List { + val searchResults: MutableList = mutableListOf() + var positions: List + var replaceContents: List? + var totalContents: String + if (chapter != null) { + viewModel.book?.let { book -> + val bookContent = BookHelp.getContent(book, chapter) + if (bookContent != null) { + //搜索替换后的正文 + withContext(Dispatchers.IO) { + chapter.title = when (AppConfig.chineseConverterType) { + 1 -> ChineseUtils.t2s(chapter.title) + 2 -> ChineseUtils.s2t(chapter.title) + else -> chapter.title + } + replaceContents = + viewModel.contentProcessor!!.getContent( + book, + chapter, + bookContent, + chineseConvert = false, + reSegment = false + ) + } + totalContents = replaceContents?.joinToString("") ?: bookContent + positions = searchPosition(totalContents, query) + var count = 1 + positions.map { + val construct = constructText(totalContents, it, query) + val result = SearchResult( + index = searchResultCounts, + indexWithinChapter = count, + text = construct[1] as String, + chapterTitle = chapter.title, + query = query, + chapterIndex = chapter.index, + newPosition = construct[0] as Int, + contentPosition = it + ) + count += 1 + searchResultCounts += 1 + searchResults.add(result) + } + } + } + } + return searchResults + } + + private fun searchPosition(content: String, pattern: String): List { + val position: MutableList = mutableListOf() + var index = content.indexOf(pattern) + while (index >= 0) { + position.add(index) + index = content.indexOf(pattern, index + 1) + } + return position + } + + private fun constructText(content: String, position: Int, query: String): Array { + // 构建关键词周边文字,在搜索结果里显示 + // todo: 判断段落,只在关键词所在段落内分割 + // todo: 利用标点符号分割完整的句 + // todo: length和设置结合,自由调整周边文字长度 + val length = 20 + var po1 = position - length + var po2 = position + query.length + length + if (po1 < 0) { + po1 = 0 + } + if (po2 > content.length) { + po2 = content.length + } + val newPosition = position - po1 + val newText = content.substring(po1, po2) + return arrayOf(newPosition, newText) + } + val isLocalBook: Boolean get() = viewModel.book?.isLocalBook() == true @@ -166,7 +251,7 @@ class SearchContentActivity : searchData.putExtra("index", searchResult.chapterIndex) searchData.putExtra("contentPosition", searchResult.contentPosition) searchData.putExtra("query", searchResult.query) - searchData.putExtra("indexWithinChapter", searchResult.resultCountWithinChapter) + searchData.putExtra("indexWithinChapter", searchResult.indexWithinChapter) setResult(RESULT_OK, searchData) finish() } diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentAdapter.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentAdapter.kt index 426f50864..364e7daaf 100644 --- a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentAdapter.kt +++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentAdapter.kt @@ -14,6 +14,7 @@ import io.legado.app.utils.hexString class SearchContentAdapter(context: Context, val callback: Callback) : RecyclerAdapter(context) { + val cacheFileNames = hashSetOf() val textColor = context.getCompatColor(R.color.primaryText).hexString.substring(2) val accentColor = context.accentColor.hexString.substring(2) diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentViewModel.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentViewModel.kt index 847e301a0..121248738 100644 --- a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentViewModel.kt @@ -2,25 +2,16 @@ package io.legado.app.ui.book.searchContent import android.app.Application -import com.github.liuyueyi.quick.transfer.ChineseUtils import io.legado.app.base.BaseViewModel import io.legado.app.data.appDb import io.legado.app.data.entities.Book -import io.legado.app.data.entities.BookChapter -import io.legado.app.help.AppConfig -import io.legado.app.help.BookHelp import io.legado.app.help.ContentProcessor -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext class SearchContentViewModel(application: Application) : BaseViewModel(application) { var bookUrl: String = "" var book: Book? = null - private var contentProcessor: ContentProcessor? = null + var contentProcessor: ContentProcessor? = null var lastQuery: String = "" - var searchResultCounts = 0 - val cacheChapterNames = hashSetOf() - val searchResultList: MutableList = mutableListOf() fun initBook(bookUrl: String, success: () -> Unit) { this.bookUrl = bookUrl @@ -34,72 +25,4 @@ class SearchContentViewModel(application: Application) : BaseViewModel(applicati } } - suspend fun searchChapter(query: String, chapter: BookChapter?): List { - val searchResultsWithinChapter: MutableList = mutableListOf() - if (chapter != null) { - book?.let { book -> - val chapterContent = BookHelp.getContent(book, chapter) - if (chapterContent != null) { - //搜索替换后的正文 - val replaceContent: String - withContext(Dispatchers.IO) { - chapter.title = when (AppConfig.chineseConverterType) { - 1 -> ChineseUtils.t2s(chapter.title) - 2 -> ChineseUtils.s2t(chapter.title) - else -> chapter.title - } - replaceContent = contentProcessor!!.getContent( - book, chapter, chapterContent, chineseConvert = false, reSegment = false - ).joinToString("") - } - val positions = searchPosition(replaceContent, query) - positions.forEachIndexed { index, position -> - val construct = getResultAndQueryIndex(replaceContent, position, query) - val result = SearchResult( - resultCountWithinChapter = index, - resultText = construct.second, - chapterTitle = chapter.title, - query = query, - chapterIndex = chapter.index, - queryIndexInResult = construct.first, - contentPosition = position - ) - searchResultsWithinChapter.add(result) - } - searchResultCounts += searchResultsWithinChapter.size - } - } - } - return searchResultsWithinChapter - } - - private fun searchPosition(content: String, pattern: String): List { - val position: MutableList = mutableListOf() - var index = content.indexOf(pattern) - while (index >= 0) { - position.add(index) - index = content.indexOf(pattern, index + 1) - } - return position - } - - private fun getResultAndQueryIndex(content: String, queryIndexInContent: Int, query: String): Pair { - // 左右移动20个字符,构建关键词周边文字,在搜索结果里显示 - // todo: 判断段落,只在关键词所在段落内分割 - // todo: 利用标点符号分割完整的句 - // todo: length和设置结合,自由调整周边文字长度 - val length = 20 - var po1 = queryIndexInContent - length - var po2 = queryIndexInContent + query.length + length - if (po1 < 0) { - po1 = 0 - } - if (po2 > content.length) { - po2 = content.length - } - val queryIndexInResult = queryIndexInContent - po1 - val newText = content.substring(po1, po2) - return queryIndexInResult to newText - } - } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchResult.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchResult.kt index 129fd6e05..3dc4e9fc3 100644 --- a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchResult.kt +++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchResult.kt @@ -4,29 +4,36 @@ import android.text.Spanned import androidx.core.text.HtmlCompat data class SearchResult( - val resultCount: Int = 0, - val resultCountWithinChapter: Int = 0, - val resultText: String = "", - val chapterTitle: String = "", + var index: Int = 0, + var indexWithinChapter: Int = 0, + var text: String = "", + var chapterTitle: String = "", val query: String, - val pageSize: Int = 0, - val chapterIndex: Int = 0, - val pageIndex: Int = 0, - val queryIndexInResult: Int = 0, - val contentPosition: Int = 0 + var pageSize: Int = 0, + var chapterIndex: Int = 0, + var pageIndex: Int = 0, + var newPosition: Int = 0, + var contentPosition: Int = 0 ) { fun getHtmlCompat(textColor: String, accentColor: String): Spanned { - val queryIndexInSurrounding = resultText.indexOf(query) - 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 = colorPresentText(newPosition, query, text, textColor, accentColor) + + "($chapterTitle)" return HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_LEGACY) } - private fun String.colorTextForHtml(textColor: String) = "$this" + private fun colorPresentText( + position: Int, + center: String, + targetText: String, + textColor: String, + accentColor: String + ): String { + val sub1 = text.substring(0, position) + val sub2 = text.substring(position + center.length, targetText.length) + return "$sub1" + + "$center" + + "$sub2" + } } \ No newline at end of file diff --git a/app/src/main/res/layout/activity_book_read.xml b/app/src/main/res/layout/activity_book_read.xml index 703c0ef5e..235c04b87 100644 --- a/app/src/main/res/layout/activity_book_read.xml +++ b/app/src/main/res/layout/activity_book_read.xml @@ -37,12 +37,6 @@ android:layout_height="match_parent" android:visibility="gone" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file