From 45c88716f3aa454fb92c0dbec23a0a3de3b94386 Mon Sep 17 00:00:00 2001 From: Horis <821938089@qq.com> Date: Wed, 7 Jun 2023 23:52:59 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/updateLog.md | 5 ++ .../java/io/legado/app/constant/AppPattern.kt | 6 ++- .../java/io/legado/app/data/dao/BookDao.kt | 3 ++ .../java/io/legado/app/data/entities/Book.kt | 18 +++++-- .../legado/app/data/entities/BookChapter.kt | 11 +++- .../java/io/legado/app/help/book/BookHelp.kt | 25 +++++++-- .../legado/app/help/book/ContentProcessor.kt | 21 ++++++-- .../main/java/io/legado/app/model/ReadBook.kt | 2 + .../ui/book/info/edit/BookInfoEditActivity.kt | 34 ++++++------ .../app/ui/book/read/ReadBookActivity.kt | 53 +++++++++++++++++-- .../app/ui/book/read/ReadBookViewModel.kt | 15 +++--- .../searchContent/SearchContentActivity.kt | 41 +++++++------- .../searchContent/SearchContentViewModel.kt | 5 +- .../io/legado/app/utils/StringExtensions.kt | 5 ++ .../io/legado/app/utils/ViewExtensions.kt | 16 +++++- 15 files changed, 195 insertions(+), 65 deletions(-) diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index 8f9ae3ab1..54dfcb53d 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -12,6 +12,11 @@ * 正文出现缺字漏字、内容缺失、排版错乱等情况,有可能是净化规则或简繁转换出现问题。 * 漫画源看书显示乱码,**阅读与其他软件的源并不通用**,请导入阅读的支持的漫画源! +**2023/06/07** + +* 修复长按菜单全文搜索结果不全或无结果问题 +* 优化全文搜索速度 + **2023/06/06** * 更新cronet: 114.0.5735.57 diff --git a/app/src/main/java/io/legado/app/constant/AppPattern.kt b/app/src/main/java/io/legado/app/constant/AppPattern.kt index 0bdcfdc1c..9f122190e 100644 --- a/app/src/main/java/io/legado/app/constant/AppPattern.kt +++ b/app/src/main/java/io/legado/app/constant/AppPattern.kt @@ -18,7 +18,7 @@ object AppPattern { val authorRegex = Regex("^\\s*作\\s*者[::\\s]+|\\s+著") val fileNameRegex = Regex("[\\\\/:*?\"<>|.]") val splitGroupRegex = Regex("[,;,;]") - val titleNumPattern = Pattern.compile("(第)(.+?)(章)") + val titleNumPattern: Pattern = Pattern.compile("(第)(.+?)(章)") //书源调试信息中的各种符号 val debugMessageSymbolRegex = Regex("[⇒◇┌└≡]") @@ -48,4 +48,8 @@ object AppPattern { val semicolonRegex = ";".toRegex() val equalsRegex = "=".toRegex() + + val spaceRegex = "\\s+".toRegex() + + val regexCharRegex = "[{}()\\[\\].+*?^$\\\\|]".toRegex() } diff --git a/app/src/main/java/io/legado/app/data/dao/BookDao.kt b/app/src/main/java/io/legado/app/data/dao/BookDao.kt index bf3b5ecee..8d0cc10d5 100644 --- a/app/src/main/java/io/legado/app/data/dao/BookDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/BookDao.kt @@ -68,6 +68,9 @@ interface BookDao { @Query("SELECT * FROM books WHERE name = :name and author = :author") fun getBook(name: String, author: String): Book? + @Query("SELECT * FROM books WHERE name = :name and origin = :origin") + fun getBookByOrigin(name: String, origin: String): Book? + @get:Query("select count(bookUrl) from books where (SELECT sum(groupId) FROM book_groups)") val noGroupSize: Int diff --git a/app/src/main/java/io/legado/app/data/entities/Book.kt b/app/src/main/java/io/legado/app/data/entities/Book.kt index dd98db86c..99a368469 100644 --- a/app/src/main/java/io/legado/app/data/entities/Book.kt +++ b/app/src/main/java/io/legado/app/data/entities/Book.kt @@ -135,6 +135,10 @@ data class Book( @IgnoredOnParcel var downloadUrls: List? = null + @Ignore + @IgnoredOnParcel + private var folderName: String? = null + fun getRealAuthor() = author.replace(AppPattern.authorRegex, "") fun getUnreadChapterNum() = max(totalChapterNum - durChapterIndex - 1, 0) @@ -237,10 +241,18 @@ data class Book( } fun getFolderName(): String { + folderName?.let { + return it + } //防止书名过长,只取9位 - var folderName = name.replace(AppPattern.fileNameRegex, "") - folderName = folderName.substring(0, min(9, folderName.length)) - return folderName + MD5Utils.md5Encode16(bookUrl) + folderName = getFolderNameNoCache() + return folderName!! + } + + fun getFolderNameNoCache(): String { + return name.replace(AppPattern.fileNameRegex, "").let { + it.substring(0, min(9, it.length)) + MD5Utils.md5Encode16(bookUrl) + } } fun toSearchBook() = SearchBook( diff --git a/app/src/main/java/io/legado/app/data/entities/BookChapter.kt b/app/src/main/java/io/legado/app/data/entities/BookChapter.kt index bb8650e3a..6e6962cf5 100644 --- a/app/src/main/java/io/legado/app/data/entities/BookChapter.kt +++ b/app/src/main/java/io/legado/app/data/entities/BookChapter.kt @@ -59,6 +59,12 @@ data class BookChapter( GSON.fromJsonObject>(variable).getOrNull() ?: hashMapOf() } + @delegate:Ignore + @IgnoredOnParcel + private val titleMD5: String by lazy { + MD5Utils.md5Encode16(title) + } + override fun putVariable(key: String, value: String?): Boolean { if (super.putVariable(key, value)) { variable = GSON.toJson(variableMap) @@ -145,9 +151,10 @@ data class BookChapter( @Suppress("unused") fun getFileName(suffix: String = "nb"): String = - String.format("%05d-%s.%s", index, MD5Utils.md5Encode16(title), suffix) + String.format("%05d-%s.%s", index, titleMD5, suffix) + @Suppress("unused") - fun getFontName(): String = String.format("%05d-%s.ttf", index, MD5Utils.md5Encode16(title)) + fun getFontName(): String = String.format("%05d-%s.ttf", index, titleMD5) } diff --git a/app/src/main/java/io/legado/app/help/book/BookHelp.kt b/app/src/main/java/io/legado/app/help/book/BookHelp.kt index b4c98e0e7..c4b858fc1 100644 --- a/app/src/main/java/io/legado/app/help/book/BookHelp.kt +++ b/app/src/main/java/io/legado/app/help/book/BookHelp.kt @@ -51,8 +51,19 @@ object BookHelp { } fun updateCacheFolder(oldBook: Book, newBook: Book) { - val oldFolderPath = FileUtils.getPath(downloadDir, cacheFolderName, oldBook.getFolderName()) - val newFolderPath = FileUtils.getPath(downloadDir, cacheFolderName, newBook.getFolderName()) + val oldFolderName = oldBook.getFolderNameNoCache() + val newFolderName = newBook.getFolderNameNoCache() + if (oldFolderName == newFolderName) return + val oldFolderPath = FileUtils.getPath( + downloadDir, + cacheFolderName, + oldFolderName + ) + val newFolderPath = FileUtils.getPath( + downloadDir, + cacheFolderName, + newFolderName + ) FileUtils.move(oldFolderPath, newFolderPath) } @@ -331,21 +342,25 @@ object BookHelp { * 设置是否禁用正文的去除重复标题,针对单个章节 */ fun setRemoveSameTitle(book: Book, bookChapter: BookChapter, removeSameTitle: Boolean) { + val fileName = bookChapter.getFileName("nr") + val contentProcessor = ContentProcessor.get(book) if (removeSameTitle) { val path = FileUtils.getPath( downloadDir, cacheFolderName, book.getFolderName(), - bookChapter.getFileName(".nr") + fileName ) + contentProcessor.removeSameTitleCache.remove(fileName) File(path).delete() } else { FileUtils.createFileIfNotExist( downloadDir, cacheFolderName, book.getFolderName(), - bookChapter.getFileName(".nr") + fileName ) + contentProcessor.removeSameTitleCache.add(fileName) } } @@ -357,7 +372,7 @@ object BookHelp { downloadDir, cacheFolderName, book.getFolderName(), - bookChapter.getFileName(".nr") + bookChapter.getFileName("nr") ) return !File(path).exists() } diff --git a/app/src/main/java/io/legado/app/help/book/ContentProcessor.kt b/app/src/main/java/io/legado/app/help/book/ContentProcessor.kt index f60717f81..7e58d3946 100644 --- a/app/src/main/java/io/legado/app/help/book/ContentProcessor.kt +++ b/app/src/main/java/io/legado/app/help/book/ContentProcessor.kt @@ -2,6 +2,7 @@ package io.legado.app.help.book import com.github.liuyueyi.quick.transfer.ChineseUtils import io.legado.app.constant.AppLog +import io.legado.app.constant.AppPattern.spaceRegex import io.legado.app.data.appDb import io.legado.app.data.entities.Book import io.legado.app.data.entities.BookChapter @@ -9,6 +10,7 @@ import io.legado.app.data.entities.ReplaceRule import io.legado.app.exception.RegexTimeoutException import io.legado.app.help.config.AppConfig import io.legado.app.help.config.ReadBookConfig +import io.legado.app.utils.escapeRegex import io.legado.app.utils.replace import io.legado.app.utils.stackTraceStr import io.legado.app.utils.toastOnUi @@ -25,7 +27,8 @@ class ContentProcessor private constructor( companion object { private val processors = hashMapOf>() - var enableRemoveSameTitle = true + + fun get(book: Book) = get(book.name, book.origin) fun get(bookName: String, bookOrigin: String): ContentProcessor { val processorWr = processors[bookName + bookOrigin] @@ -47,9 +50,11 @@ class ContentProcessor private constructor( private val titleReplaceRules = CopyOnWriteArrayList() private val contentReplaceRules = CopyOnWriteArrayList() + val removeSameTitleCache = hashSetOf() init { upReplaceRules() + upRemoveSameTitle() } fun upReplaceRules() { @@ -63,6 +68,15 @@ class ContentProcessor private constructor( } } + fun upRemoveSameTitle() { + val book = appDb.bookDao.getBookByOrigin(bookName, bookOrigin) ?: return + removeSameTitleCache.clear() + val files = BookHelp.getChapterFiles(book).filter { + it.endsWith("nr") + } + removeSameTitleCache.addAll(files) + } + fun getTitleReplaceRules(): List { return titleReplaceRules } @@ -85,9 +99,10 @@ class ContentProcessor private constructor( var sameTitleRemoved = false if (content != "null") { //去除重复标题 - if (enableRemoveSameTitle && BookHelp.removeSameTitle(book, chapter)) try { + val fileName = chapter.getFileName("nr") + if (!removeSameTitleCache.contains(fileName)) try { val name = Pattern.quote(book.name) - var title = Pattern.quote(chapter.title) + var title = chapter.title.escapeRegex().replace(spaceRegex, "\\\\s*") var matcher = Pattern.compile("^(\\s|\\p{P}|${name})*${title}(\\s)*") .matcher(mContent) if (matcher.find()) { diff --git a/app/src/main/java/io/legado/app/model/ReadBook.kt b/app/src/main/java/io/legado/app/model/ReadBook.kt index 3a29aff8e..3847feed5 100644 --- a/app/src/main/java/io/legado/app/model/ReadBook.kt +++ b/app/src/main/java/io/legado/app/model/ReadBook.kt @@ -54,6 +54,7 @@ object ReadBook : CoroutineScope by MainScope() { var preDownloadTask: Coroutine<*>? = null val downloadedChapters = hashSetOf() + var contentProcessor: ContentProcessor? = null //暂时保存跳转前进度 fun saveCurrentBookProcess() { @@ -74,6 +75,7 @@ object ReadBook : CoroutineScope by MainScope() { readRecord.bookName = book.name readRecord.readTime = appDb.readRecordDao.getReadTime(book.name) ?: 0 chapterSize = appDb.bookChapterDao.getChapterCount(book.bookUrl) + contentProcessor = ContentProcessor.get(book) durChapterIndex = book.durChapterIndex durChapterPos = book.durChapterPos isLocalBook = book.isLocal diff --git a/app/src/main/java/io/legado/app/ui/book/info/edit/BookInfoEditActivity.kt b/app/src/main/java/io/legado/app/ui/book/info/edit/BookInfoEditActivity.kt index fb6e13c79..b497dcdc9 100644 --- a/app/src/main/java/io/legado/app/ui/book/info/edit/BookInfoEditActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/info/edit/BookInfoEditActivity.kt @@ -11,6 +11,7 @@ import io.legado.app.base.VMBaseActivity import io.legado.app.constant.BookType import io.legado.app.data.entities.Book import io.legado.app.databinding.ActivityBookInfoEditBinding +import io.legado.app.help.book.BookHelp import io.legado.app.help.book.isAudio import io.legado.app.help.book.isImage import io.legado.app.help.book.isLocal @@ -94,22 +95,23 @@ class BookInfoEditActivity : } private fun saveData() = binding.run { - viewModel.book?.let { book -> - book.name = tieBookName.text?.toString() ?: "" - book.author = tieBookAuthor.text?.toString() ?: "" - val local = if (book.isLocal) BookType.local else 0 - book.type = when (spType.selectedItemPosition) { - 2 -> BookType.image or local - 1 -> BookType.audio or local - else -> BookType.text or local - } - val customCoverUrl = tieCoverUrl.text?.toString() - book.customCoverUrl = if (customCoverUrl == book.coverUrl) null else customCoverUrl - book.customIntro = tieBookIntro.text?.toString() - viewModel.saveBook(book) { - setResult(Activity.RESULT_OK) - finish() - } + val book = viewModel.book ?: return@run + val oldBook = book.copy() + book.name = tieBookName.text?.toString() ?: "" + book.author = tieBookAuthor.text?.toString() ?: "" + val local = if (book.isLocal) BookType.local else 0 + book.type = when (spType.selectedItemPosition) { + 2 -> BookType.image or local + 1 -> BookType.audio or local + else -> BookType.text or local + } + val customCoverUrl = tieCoverUrl.text?.toString() + book.customCoverUrl = if (customCoverUrl == book.coverUrl) null else customCoverUrl + book.customIntro = tieBookIntro.text?.toString() + BookHelp.updateCacheFolder(oldBook, book) + viewModel.saveBook(book) { + setResult(Activity.RESULT_OK) + finish() } } 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 cc665f404..0afc84ad8 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 @@ -121,7 +121,6 @@ class ReadBookActivity : BaseReadBookActivity(), val searchResult = IntentData.get("searchResult$key") val searchResultList = IntentData.get>("searchResultList$key") if (searchResult != null && searchResultList != null) { - ContentProcessor.enableRemoveSameTitle = false viewModel.searchContentQuery = searchResult.query binding.searchMenu.upSearchResultList(searchResultList) isShowingSearchResult = true @@ -294,6 +293,7 @@ class ReadBookActivity : BaseReadBookActivity(), item.isVisible = BuildConfig.DEBUG item.isChecked = AppConfig.enableReview } + R.id.menu_reverse_content -> item.isVisible = onLine } } @@ -318,6 +318,7 @@ class ReadBookActivity : BaseReadBookActivity(), showDialogFragment(ChangeBookSourceDialog(it.name, it.author)) } } + R.id.menu_chapter_change_source -> launch { val book = ReadBook.book ?: return@launch val chapter = @@ -328,6 +329,7 @@ class ReadBookActivity : BaseReadBookActivity(), ChangeChapterSourceDialog(book.name, book.author, chapter.index, chapter.title) ) } + R.id.menu_refresh, R.id.menu_refresh_dur -> { if (ReadBook.bookSource == null) { @@ -340,6 +342,7 @@ class ReadBookActivity : BaseReadBookActivity(), } } } + R.id.menu_refresh_after -> { if (ReadBook.bookSource == null) { upContent() @@ -351,6 +354,7 @@ class ReadBookActivity : BaseReadBookActivity(), } } } + R.id.menu_refresh_all -> { if (ReadBook.bookSource == null) { upContent() @@ -362,6 +366,7 @@ class ReadBookActivity : BaseReadBookActivity(), } } } + R.id.menu_download -> showDownloadDialog() R.id.menu_add_bookmark -> addBookmark() R.id.menu_edit_content -> showDialogFragment(ContentEditDialog()) @@ -371,28 +376,34 @@ class ReadBookActivity : BaseReadBookActivity(), } loadChapterList(it) } + R.id.menu_enable_replace -> changeReplaceRuleState() R.id.menu_re_segment -> ReadBook.book?.let { it.setReSegment(!it.getReSegment()) menu?.findItem(R.id.menu_re_segment)?.isChecked = it.getReSegment() ReadBook.loadContent(false) } + R.id.menu_enable_review -> { AppConfig.enableReview = !AppConfig.enableReview menu?.findItem(R.id.menu_enable_review)?.isChecked = AppConfig.enableReview ReadBook.loadContent(false) } + R.id.menu_page_anim -> showPageAnimConfig { binding.readView.upPageAnim() ReadBook.loadContent(false) } + R.id.menu_log -> showDialogFragment() R.id.menu_toc_regex -> showDialogFragment( TxtTocRuleDialog(ReadBook.book?.tocUrl) ) + R.id.menu_reverse_content -> ReadBook.book?.let { viewModel.reverseContent(it) } + R.id.menu_set_charset -> showCharsetConfig() R.id.menu_image_style -> { val imgStyles = @@ -405,12 +416,29 @@ class ReadBookActivity : BaseReadBookActivity(), ReadBook.loadContent(false) } } + R.id.menu_get_progress -> ReadBook.book?.let { viewModel.syncBookProgress(it) { progress -> sureSyncProgress(progress) } } - R.id.menu_same_title_removed -> viewModel.reverseRemoveSameTitle() + + R.id.menu_same_title_removed -> { + ReadBook.book?.let { + val contentProcessor = ContentProcessor.get(it) + val textChapter = ReadBook.curTextChapter + if (textChapter != null + && !textChapter.sameTitleRemoved + && !contentProcessor.removeSameTitleCache.contains( + textChapter.chapter.getFileName("nr") + ) + ) { + toastOnUi("未找到可移除的重复标题") + } + } + viewModel.reverseRemoveSameTitle() + } + R.id.menu_help -> showReadMenuHelp() } return super.onCompatOptionsItemSelected(item) @@ -477,34 +505,41 @@ class ReadBookActivity : BaseReadBookActivity(), return true } } + isNextKey(keyCode) -> { if (keyCode != KeyEvent.KEYCODE_UNKNOWN) { binding.readView.pageDelegate?.keyTurnPage(PageDirection.NEXT) return true } } + keyCode == KeyEvent.KEYCODE_VOLUME_UP -> { if (volumeKeyPage(PageDirection.PREV)) { return true } } + keyCode == KeyEvent.KEYCODE_VOLUME_DOWN -> { if (volumeKeyPage(PageDirection.NEXT)) { return true } } + keyCode == KeyEvent.KEYCODE_PAGE_UP -> { binding.readView.pageDelegate?.keyTurnPage(PageDirection.PREV) return true } + keyCode == KeyEvent.KEYCODE_PAGE_DOWN -> { binding.readView.pageDelegate?.keyTurnPage(PageDirection.NEXT) return true } + keyCode == KeyEvent.KEYCODE_SPACE -> { binding.readView.pageDelegate?.keyTurnPage(PageDirection.NEXT) return true } + keyCode == KeyEvent.KEYCODE_BACK -> { if (isShowingSearchResult) { exitSearchMenu() @@ -544,6 +579,7 @@ class ReadBookActivity : BaseReadBookActivity(), return true } } + KeyEvent.KEYCODE_BACK -> { event?.let { if ((event.flags and KeyEvent.FLAG_CANCELED_LONG_PRESS == 0) @@ -585,12 +621,14 @@ class ReadBookActivity : BaseReadBookActivity(), event.rawX + cursorLeft.width, event.rawY - cursorLeft.height ) + R.id.cursor_right -> readView.curPage.selectEndMove( event.rawX - cursorRight.width, event.rawY - cursorRight.height ) } } + MotionEvent.ACTION_UP -> showTextActionMenu() } return true @@ -657,6 +695,7 @@ class ReadBookActivity : BaseReadBookActivity(), 1 -> binding.readView.aloudStartSelect() else -> speak(binding.readView.getSelectText()) } + R.id.menu_bookmark -> binding.readView.curPage.let { val bookmark = it.createBookmark() if (bookmark == null) { @@ -666,6 +705,7 @@ class ReadBookActivity : BaseReadBookActivity(), } return true } + R.id.menu_replace -> { val scopes = arrayListOf() ReadBook.book?.name?.let { @@ -683,11 +723,13 @@ class ReadBookActivity : BaseReadBookActivity(), ) return true } + R.id.menu_search_content -> { viewModel.searchContentQuery = selectedText openSearchActivity(selectedText) return true } + R.id.menu_dict -> { showDialogFragment(DictDialog(selectedText)) return true @@ -1010,7 +1052,6 @@ class ReadBookActivity : BaseReadBookActivity(), override fun exitSearchMenu() { if (isShowingSearchResult) { isShowingSearchResult = false - ContentProcessor.enableRemoveSameTitle = true binding.searchMenu.invalidate() binding.searchMenu.invisible() binding.readView.isTextSelected = false @@ -1110,6 +1151,7 @@ class ReadBookActivity : BaseReadBookActivity(), ReadAloud.upReadAloudClass() ReadBook.readAloud() } + BaseReadAloudService.pause -> ReadAloud.resume(this) else -> ReadAloud.pause(this) } @@ -1142,6 +1184,7 @@ class ReadBookActivity : BaseReadBookActivity(), viewModel.saveImage(src, Uri.parse(path)) } } + "selectFolder" -> selectImageDir.launch() } popupAction.dismiss() @@ -1164,15 +1207,18 @@ class ReadBookActivity : BaseReadBookActivity(), setCurTextColor(color) postEvent(EventBus.UP_CONFIG, false) } + BG_COLOR -> { setCurBg(0, "#${color.hexString}") postEvent(EventBus.UP_CONFIG, false) } + TIP_COLOR -> { ReadTipConfig.tipColor = color postEvent(EventBus.TIP_COLOR, "") postEvent(EventBus.UP_CONFIG, true) } + TIP_DIVIDER_COLOR -> { ReadTipConfig.tipDividerColor = color postEvent(EventBus.TIP_COLOR, "") @@ -1233,6 +1279,7 @@ class ReadBookActivity : BaseReadBookActivity(), lineIndex, charIndex + viewModel.searchContentQuery.length - 1 ) + 1 -> binding.readView.curPage.selectEndMoveIndex( 0, lineIndex + 1, charIndex2 ) diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt index 8f951719b..d04ddfad2 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt @@ -155,6 +155,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) { is SecurityException, is FileNotFoundException -> { permissionDenialLiveData.postValue(1) } + else -> { AppLog.put("LoadTocError:${it.localizedMessage}", it) ReadBook.upMsg("LoadTocError:${it.localizedMessage}") @@ -434,14 +435,12 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) { */ fun reverseRemoveSameTitle() { execute { - val book = ReadBook.book - val textChapter = ReadBook.curTextChapter - if (book != null && textChapter != null) { - BookHelp.setRemoveSameTitle( - book, textChapter.chapter, !textChapter.sameTitleRemoved - ) - ReadBook.loadContent(ReadBook.durChapterIndex) - } + val book = ReadBook.book ?: return@execute + val textChapter = ReadBook.curTextChapter ?: return@execute + BookHelp.setRemoveSameTitle( + book, textChapter.chapter, !textChapter.sameTitleRemoved + ) + ReadBook.loadContent(ReadBook.durChapterIndex) } } 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 8bd83884a..e96740562 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 @@ -27,8 +27,10 @@ import io.legado.app.ui.widget.recycler.UpLinearLayoutManager import io.legado.app.ui.widget.recycler.VerticalDivider import io.legado.app.utils.* import io.legado.app.utils.viewbindingdelegate.viewBinding +import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Job +import kotlinx.coroutines.async import kotlinx.coroutines.ensureActive import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -47,6 +49,7 @@ class SearchContentActivity : } private var durChapterIndex = 0 private var searchJob: Job? = null + private var initJob: Deferred<*>? = null override fun onActivityCreated(savedInstanceState: Bundle?) { val bbg = bottomBackground @@ -56,38 +59,37 @@ class SearchContentActivity : binding.ivSearchContentTop.setColorFilter(btc) binding.ivSearchContentBottom.setColorFilter(btc) val searchResultList = IntentData.get>("searchResultList") + val position = intent.getIntExtra("searchResultIndex", 0) val noSearchResult = searchResultList == null initSearchView(!noSearchResult) initRecyclerView() initView() - 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(noSearchResult) - } + val bookUrl = intent.getStringExtra("bookUrl") ?: return + viewModel.initBook(bookUrl) { + initSearchResultList(searchResultList, position) + initBook(noSearchResult) } } override fun dispatchTouchEvent(ev: MotionEvent): Boolean { if (ev.action == MotionEvent.ACTION_DOWN) { - searchView.post { - currentFocus?.let { - if (it is EditText) { - it.clearFocus() - it.hideSoftInput() - } + currentFocus?.let { + if (it.shouldHideSoftInput(ev)) { + it.hideSoftInput() } } } return super.dispatchTouchEvent(ev) } + private fun initSearchResultList(list: List?, position: Int) { + list ?: return + viewModel.searchResultList.addAll(list) + viewModel.searchResultCounts = list.size + adapter.setItems(list) + binding.recyclerView.scrollToPosition(position) + } + private fun initSearchView(clearFocus: Boolean) { searchView.applyTint(primaryTextColor) searchView.onActionViewExpanded() @@ -149,7 +151,7 @@ class SearchContentActivity : } private fun initCacheFileNames(book: Book) { - launch { + initJob = async { withContext(IO) { viewModel.cacheChapterNames.addAll(BookHelp.getChapterFiles(book)) } @@ -179,8 +181,8 @@ class SearchContentActivity : viewModel.lastQuery = query binding.refreshProgressBar.isAutoLoading = true binding.fbStop.visible() - ContentProcessor.enableRemoveSameTitle = false searchJob = launch(IO) { + initJob?.await() kotlin.runCatching { appDb.bookChapterDao.getChapterList(viewModel.bookUrl).forEach { bookChapter -> ensureActive() @@ -211,7 +213,6 @@ class SearchContentActivity : }.onFailure { AppLog.put("全文搜索出错\n${it.localizedMessage}", it) } - ContentProcessor.enableRemoveSameTitle = true binding.tvCurrentSearchInfo.post { binding.fbStop.invisible() binding.refreshProgressBar.isAutoLoading = false 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 22bb7de9d..e510c026a 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 @@ -18,7 +18,7 @@ import kotlin.coroutines.coroutineContext 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() @@ -38,10 +38,9 @@ class SearchContentViewModel(application: Application) : BaseViewModel(applicati suspend fun searchChapter( query: String, - chapter: BookChapter? + chapter: BookChapter ): List { val searchResultsWithinChapter: MutableList = mutableListOf() - chapter ?: return searchResultsWithinChapter val book = book ?: return searchResultsWithinChapter val chapterContent = BookHelp.getContent(book, chapter) ?: return searchResultsWithinChapter coroutineContext.ensureActive() diff --git a/app/src/main/java/io/legado/app/utils/StringExtensions.kt b/app/src/main/java/io/legado/app/utils/StringExtensions.kt index 60a6403a0..e5f262f5b 100644 --- a/app/src/main/java/io/legado/app/utils/StringExtensions.kt +++ b/app/src/main/java/io/legado/app/utils/StringExtensions.kt @@ -7,6 +7,7 @@ import android.icu.text.Collator import android.icu.util.ULocale import android.net.Uri import android.text.Editable +import io.legado.app.constant.AppPattern import io.legado.app.constant.AppPattern.dataUriRegex import java.io.File import java.lang.Character.codePointCount @@ -127,3 +128,7 @@ fun CharSequence.toStringArray(): Array { } } +fun String.escapeRegex(): String { + return replace(AppPattern.regexCharRegex, "\\\\$0") +} + diff --git a/app/src/main/java/io/legado/app/utils/ViewExtensions.kt b/app/src/main/java/io/legado/app/utils/ViewExtensions.kt index 1b0ab17fa..cc8e6cd94 100644 --- a/app/src/main/java/io/legado/app/utils/ViewExtensions.kt +++ b/app/src/main/java/io/legado/app/utils/ViewExtensions.kt @@ -8,6 +8,7 @@ import android.graphics.Bitmap import android.graphics.Canvas import android.os.Build import android.text.Html +import android.view.MotionEvent import android.view.View import android.view.View.* import android.view.inputmethod.InputMethodManager @@ -195,4 +196,17 @@ fun PopupMenu.show(x: Int, y: Int) { }.onFailure { it.printOnDebug() } -} \ No newline at end of file +} + +fun View.shouldHideSoftInput(event: MotionEvent): Boolean { + if (this is EditText) { + val l = intArrayOf(0, 0) + getLocationInWindow(l) + val left = l[0] + val top = l[1] + val bottom = top + getHeight() + val right = left + getWidth() + return !(event.x > left && event.x < right && event.y > top && event.y < bottom) + } + return false +}