diff --git a/app/src/main/java/io/legado/app/data/dao/ReplaceRuleDao.kt b/app/src/main/java/io/legado/app/data/dao/ReplaceRuleDao.kt index 5643ef975..ad374f5c9 100644 --- a/app/src/main/java/io/legado/app/data/dao/ReplaceRuleDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/ReplaceRuleDao.kt @@ -32,15 +32,6 @@ interface ReplaceRuleDao { @Query("SELECT * FROM replace_rules WHERE id in (:ids)") fun findByIds(vararg ids: Long): List - @Query( - """ - SELECT * FROM replace_rules WHERE isEnabled = 1 - AND (scope LIKE '%' || :scope || '%' or scope is null or scope = '') - order by sortOrder - """ - ) - fun findEnabledByScope(scope: String): List - @Query( """ SELECT * FROM replace_rules WHERE isEnabled = 1 diff --git a/app/src/main/java/io/legado/app/help/BookHelp.kt b/app/src/main/java/io/legado/app/help/BookHelp.kt index a8c4235d4..a9364536f 100644 --- a/app/src/main/java/io/legado/app/help/BookHelp.kt +++ b/app/src/main/java/io/legado/app/help/BookHelp.kt @@ -1,22 +1,17 @@ package io.legado.app.help -import com.hankcs.hanlp.HanLP import io.legado.app.App import io.legado.app.constant.AppPattern import io.legado.app.constant.EventBus import io.legado.app.data.entities.Book import io.legado.app.data.entities.BookChapter -import io.legado.app.data.entities.ReplaceRule import io.legado.app.help.coroutine.Coroutine import io.legado.app.model.analyzeRule.AnalyzeUrl import io.legado.app.model.localBook.LocalBook import io.legado.app.utils.* -import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.delay -import kotlinx.coroutines.withContext import net.ricecode.similarity.JaroWinklerStrategy import net.ricecode.similarity.StringSimilarityServiceImpl -import org.jetbrains.anko.toast import java.io.File import java.util.concurrent.CopyOnWriteArraySet import java.util.regex.Matcher @@ -317,90 +312,4 @@ object BookHelp { .replace(regexOther, "") } - private var bookName: String? = null - private var bookOrigin: String? = null - private var replaceRules: List = arrayListOf() - - @Synchronized - fun upReplaceRules() { - val o = bookOrigin - bookName?.let { - replaceRules = if (o.isNullOrEmpty()) { - App.db.replaceRuleDao().findEnabledByScope(it) - } else { - App.db.replaceRuleDao().findEnabledByScope(it, o) - } - } - } - - suspend fun disposeContent( - book: Book, - title: String, - content: String, - ): List { - var title1 = title - var content1 = content - if (book.getUseReplaceRule()) { - synchronized(this) { - if (bookName != book.name || bookOrigin != book.origin) { - bookName = book.name - bookOrigin = book.origin - replaceRules = if (bookOrigin.isNullOrEmpty()) { - App.db.replaceRuleDao().findEnabledByScope(bookName!!) - } else { - App.db.replaceRuleDao().findEnabledByScope(bookName!!, bookOrigin!!) - } - } - } - replaceRules.forEach { item -> - item.pattern.let { - if (it.isNotEmpty()) { - try { - content1 = if (item.isRegex) { - content1.replace(it.toRegex(), item.replacement) - } else { - content1.replace(it, item.replacement) - } - } catch (e: Exception) { - withContext(Main) { - App.INSTANCE.toast("${item.name}替换出错") - } - } - } - } - } - } - if (book.getReSegment()) { - content1 = ContentHelp.reSegment(content1, title1) - } - try { - when (AppConfig.chineseConverterType) { - 1 -> { - title1 = HanLP.convertToSimplifiedChinese(title1) - content1 = HanLP.convertToSimplifiedChinese(content1) - } - 2 -> { - title1 = HanLP.convertToTraditionalChinese(title1) - content1 = HanLP.convertToTraditionalChinese(content1) - } - } - } catch (e: Exception) { - withContext(Main) { - App.INSTANCE.toast("简繁转换出错") - } - } - val contents = arrayListOf() - content1.split("\n").forEach { - val str = it.replace("^[\\n\\s\\r]+".toRegex(), "") - if (contents.isEmpty()) { - contents.add(title1) - if (str != title1 && str.isNotEmpty()) { - contents.add("${ReadBookConfig.paragraphIndent}$str") - } - } else if (str.isNotEmpty()) { - contents.add("${ReadBookConfig.paragraphIndent}$str") - } - } - return contents - } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/help/ContentProcessor.kt b/app/src/main/java/io/legado/app/help/ContentProcessor.kt new file mode 100644 index 000000000..72a4361d5 --- /dev/null +++ b/app/src/main/java/io/legado/app/help/ContentProcessor.kt @@ -0,0 +1,81 @@ +package io.legado.app.help + +import com.hankcs.hanlp.HanLP +import io.legado.app.App +import io.legado.app.data.entities.Book +import io.legado.app.data.entities.ReplaceRule +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import org.jetbrains.anko.toast + +class ContentProcessor(private val bookName: String, private val bookOrigin: String) { + + private var replaceRules = arrayListOf() + + init { + upReplaceRules() + } + + @Synchronized + fun upReplaceRules() { + replaceRules.clear() + replaceRules.addAll(App.db.replaceRuleDao().findEnabledByScope(bookName, bookOrigin)) + } + + suspend fun getContent( + book: Book, + title: String, //已经经过简繁转换 + content: String, + isRead: Boolean = true + ): List { + var content1 = content + if (book.getUseReplaceRule()) { + replaceRules.forEach { item -> + item.pattern.let { + if (it.isNotEmpty()) { + try { + content1 = if (item.isRegex) { + content1.replace(it.toRegex(), item.replacement) + } else { + content1.replace(it, item.replacement) + } + } catch (e: Exception) { + withContext(Dispatchers.Main) { + App.INSTANCE.toast("${item.name}替换出错") + } + } + } + } + } + } + if (isRead) { + if (book.getReSegment()) { + content1 = ContentHelp.reSegment(content1, title) + } + try { + when (AppConfig.chineseConverterType) { + 1 -> content1 = HanLP.convertToSimplifiedChinese(content1) + 2 -> content1 = HanLP.convertToTraditionalChinese(content1) + } + } catch (e: Exception) { + withContext(Dispatchers.Main) { + App.INSTANCE.toast("简繁转换出错") + } + } + } + val contents = arrayListOf() + content1.split("\n").forEach { + val str = it.replace("^[\\n\\s\\r]+".toRegex(), "") + if (contents.isEmpty()) { + contents.add(title) + if (str != title && str.isNotEmpty()) { + contents.add("${ReadBookConfig.paragraphIndent}$str") + } + } else if (str.isNotEmpty()) { + contents.add("${ReadBookConfig.paragraphIndent}$str") + } + } + return contents + } + +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/service/help/ReadBook.kt b/app/src/main/java/io/legado/app/service/help/ReadBook.kt index 3ed6c7809..561155843 100644 --- a/app/src/main/java/io/legado/app/service/help/ReadBook.kt +++ b/app/src/main/java/io/legado/app/service/help/ReadBook.kt @@ -8,10 +8,7 @@ import io.legado.app.data.entities.Book import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookSource import io.legado.app.data.entities.ReadRecord -import io.legado.app.help.AppConfig -import io.legado.app.help.BookHelp -import io.legado.app.help.IntentDataHelp -import io.legado.app.help.ReadBookConfig +import io.legado.app.help.* import io.legado.app.help.coroutine.Coroutine import io.legado.app.model.webBook.WebBook import io.legado.app.service.BaseReadAloudService @@ -30,6 +27,7 @@ import org.jetbrains.anko.toast object ReadBook { var titleDate = MutableLiveData() var book: Book? = null + var contentProcessor: ContentProcessor? = null var inBookshelf = false var chapterSize = 0 var durChapterIndex = 0 @@ -48,6 +46,7 @@ object ReadBook { fun resetData(book: Book) { this.book = book + contentProcessor = ContentProcessor(book.name, book.origin) readRecord.bookName = book.name readRecord.readTime = App.db.readRecordDao().getReadTime(book.name) ?: 0 durChapterIndex = book.durChapterIndex @@ -372,13 +371,14 @@ object ReadBook { resetPageOffset: Boolean ) { Coroutine.async { + ImageProvider.clearOut(durChapterIndex) if (chapter.index in durChapterIndex - 1..durChapterIndex + 1) { chapter.title = when (AppConfig.chineseConverterType) { 1 -> HanLP.convertToSimplifiedChinese(chapter.title) 2 -> HanLP.convertToTraditionalChinese(chapter.title) else -> chapter.title } - val contents = BookHelp.disposeContent(book, chapter.title, content) + val contents = contentProcessor!!.getContent(book, chapter.title, content) when (chapter.index) { durChapterIndex -> { curTextChapter = @@ -393,7 +393,6 @@ object ReadBook { callBack?.upView() curPageChanged() callBack?.contentLoadFinish() - ImageProvider.clearOut(durChapterIndex) } durChapterIndex - 1 -> { prevTextChapter = diff --git a/app/src/main/java/io/legado/app/ui/book/cache/CacheViewModel.kt b/app/src/main/java/io/legado/app/ui/book/cache/CacheViewModel.kt index 5604f4599..be988fe08 100644 --- a/app/src/main/java/io/legado/app/ui/book/cache/CacheViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/cache/CacheViewModel.kt @@ -10,6 +10,7 @@ import io.legado.app.constant.AppPattern import io.legado.app.constant.PreferKey import io.legado.app.data.entities.Book import io.legado.app.help.BookHelp +import io.legado.app.help.ContentProcessor import io.legado.app.help.storage.WebDavHelp import io.legado.app.utils.* import java.io.File @@ -35,12 +36,12 @@ class CacheViewModel(application: Application) : BaseViewModel(application) { } } - private fun export(doc: DocumentFile, book: Book) { + private suspend fun export(doc: DocumentFile, book: Book) { val filename = "${book.name} by ${book.author}.txt" val content = getAllContents(book) DocumentUtils.createFileIfNotExist(doc, filename) ?.writeText(context, content) - if(App.INSTANCE.getPrefBoolean(PreferKey.webDavCacheBackup,false)) { + if (App.INSTANCE.getPrefBoolean(PreferKey.webDavCacheBackup, false)) { FileUtils.createFileIfNotExist( File(FileUtils.getCachePath()), filename @@ -50,74 +51,75 @@ class CacheViewModel(application: Application) : BaseViewModel(application) { // 上传完删除cache文件 FileUtils.deleteFile("${FileUtils.getCachePath()}${File.separator}${filename}") } - App.db.bookChapterDao().getChapterList(book.bookUrl).forEach { chapter -> - BookHelp.getContent(book, chapter).let { content -> - content?.split("\n")?.forEachIndexed { index, text -> - val matcher = AppPattern.imgPattern.matcher(text) - if (matcher.find()) { - var src = matcher.group(1) - src = NetworkUtils.getAbsoluteURL(chapter.url, src) - src?.let { - val vFile = BookHelp.getImage(book, src) - if (vFile.exists()) { - DocumentUtils.createFileIfNotExist(doc, - "${index}-${MD5Utils.md5Encode16(src)}.jpg", - subDirs = arrayOf("${book.name}_${book.author}", - "images", - chapter.title)) - ?.writeBytes(context, vFile.readBytes()) - } - } - } - } + getSrcList(book).forEach { + val vFile = BookHelp.getImage(book, it.third) + if (vFile.exists()) { + DocumentUtils.createFileIfNotExist( + doc, + "${it.second}-${MD5Utils.md5Encode16(it.third)}.jpg", + subDirs = arrayOf("${book.name}_${book.author}", "images", it.first) + )?.writeBytes(context, vFile.readBytes()) } } } - private fun export(file: File, book: Book) { + private suspend fun export(file: File, book: Book) { val filename = "${book.name} by ${book.author}.txt" FileUtils.createFileIfNotExist(file, filename) .writeText(getAllContents(book)) - if(App.INSTANCE.getPrefBoolean(PreferKey.webDavCacheBackup,false)) { + if (App.INSTANCE.getPrefBoolean(PreferKey.webDavCacheBackup, false)) { WebDavHelp.exportWebDav(file.absolutePath, filename) // 导出到webdav } - App.db.bookChapterDao().getChapterList(book.bookUrl).forEach { chapter -> - BookHelp.getContent(book, chapter).let { content -> - content?.split("\n")?.forEachIndexed { index, text -> - val matcher = AppPattern.imgPattern.matcher(text) - if (matcher.find()) { - var src = matcher.group(1) - src = NetworkUtils.getAbsoluteURL(chapter.url, src) - src?.let { - val vFile = BookHelp.getImage(book, src) - if (vFile.exists()) { - FileUtils.createFileIfNotExist(file, - "${book.name}_${book.author}", - "images", - chapter.title, - "${index}-${MD5Utils.md5Encode16(src)}.jpg") - .writeBytes(vFile.readBytes()) - } - } - } - } + getSrcList(book).forEach { + val vFile = BookHelp.getImage(book, it.third) + if (vFile.exists()) { + FileUtils.createFileIfNotExist( + file, + "${book.name}_${book.author}", + "images", + it.first, + "${it.second}-${MD5Utils.md5Encode16(it.third)}.jpg" + ).writeBytes(vFile.readBytes()) } } } - private fun getAllContents(book: Book): String { + private suspend fun getAllContents(book: Book): String { + val contentProcessor = ContentProcessor(book.name, book.origin) val stringBuilder = StringBuilder() stringBuilder.append(book.name) .append("\n") .append(context.getString(R.string.author_show, book.author)) App.db.bookChapterDao().getChapterList(book.bookUrl).forEach { chapter -> - BookHelp.getContent(book, chapter).let { + BookHelp.getContent(book, chapter).let { content -> + val content1 = content?.let { + contentProcessor.getContent(book, chapter.title, it, false) + } stringBuilder.append("\n\n") .append(chapter.title) .append("\n") - .append(it) + .append(content1) } } return stringBuilder.toString() } + + private fun getSrcList(book: Book): ArrayList> { + val srcList = arrayListOf>() + App.db.bookChapterDao().getChapterList(book.bookUrl).forEach { chapter -> + BookHelp.getContent(book, chapter)?.let { content -> + content.split("\n").forEachIndexed { index, text -> + val matcher = AppPattern.imgPattern.matcher(text) + if (matcher.find()) { + var src = matcher.group(1) + src = NetworkUtils.getAbsoluteURL(chapter.url, src) + src?.let { + srcList.add(Triple(chapter.title, index, src)) + } + } + } + } + } + return srcList + } } \ No newline at end of file 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 64087032b..66245bc6e 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 @@ -276,7 +276,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) { */ fun replaceRuleChanged() { execute { - BookHelp.upReplaceRules() + ReadBook.contentProcessor?.upReplaceRules() ReadBook.loadContent(resetPageOffset = false) } } 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 6218c900f..5a02ffa4a 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 @@ -24,7 +24,9 @@ import io.legado.app.utils.getViewModel import io.legado.app.utils.observeEvent import kotlinx.android.synthetic.main.activity_search_content.* import kotlinx.android.synthetic.main.view_search.* -import kotlinx.coroutines.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.jetbrains.anko.sdk27.listeners.onClick @@ -34,7 +36,6 @@ class SearchContentActivity : override val viewModel: SearchContentViewModel get() = getViewModel(SearchContentViewModel::class.java) - lateinit var adapter: SearchContentAdapter private lateinit var mLayoutManager: UpLinearLayoutManager private var searchResultCounts = 0 @@ -97,14 +98,12 @@ class SearchContentActivity : @SuppressLint("SetTextI18n") private fun initBook() { - launch { - tv_current_search_info.text = "搜索结果:$searchResultCounts" - viewModel.book?.let { - initCacheFileNames(it) - durChapterIndex = it.durChapterIndex - intent.getStringExtra("searchWord")?.let { searchWord -> - search_view.setQuery(searchWord, true) - } + tv_current_search_info.text = "搜索结果:$searchResultCounts" + viewModel.book?.let { + initCacheFileNames(it) + durChapterIndex = it.durChapterIndex + intent.getStringExtra("searchWord")?.let { searchWord -> + search_view.setQuery(searchWord, true) } } } @@ -141,14 +140,13 @@ class SearchContentActivity : var searchResults = listOf() launch(Dispatchers.Main) { App.db.bookChapterDao().getChapterList(viewModel.bookUrl).map { chapter -> - val job = async(Dispatchers.IO) { + withContext(Dispatchers.IO) { if (isLocalBook || adapter.cacheFileNames.contains(chapter.getFileName()) ) { searchResults = searchChapter(newText, chapter) } } - job.await() if (searchResults.isNotEmpty()) { searchResultList.addAll(searchResults) refresh_progress_bar.isAutoLoading = false @@ -164,26 +162,27 @@ class SearchContentActivity : private suspend fun searchChapter(query: String, chapter: BookChapter?): List { val searchResults: MutableList = mutableListOf() var positions: List - var replaceContents: List? = null + var replaceContents: List? var totalContents: String if (chapter != null) { viewModel.book?.let { book -> val bookContent = BookHelp.getContent(book, chapter) if (bookContent != null) { //搜索替换后的正文 - val job = async(Dispatchers.IO) { + withContext(Dispatchers.IO) { chapter.title = when (AppConfig.chineseConverterType) { 1 -> HanLP.convertToSimplifiedChinese(chapter.title) 2 -> HanLP.convertToTraditionalChinese(chapter.title) else -> chapter.title } - replaceContents = BookHelp.disposeContent(book, chapter.title, bookContent) + replaceContents = + viewModel.contentProcessor!!.getContent( + book, + chapter.title, + bookContent + ) } - job.await() - while (replaceContents == null) { - delay(100L) - } - totalContents = replaceContents!!.joinToString("") + totalContents = replaceContents?.joinToString("") ?: bookContent positions = searchPosition(totalContents, query) var count = 1 positions.map { @@ -241,7 +240,6 @@ class SearchContentActivity : get() = viewModel.book?.isLocalBook() == true override fun openSearchResult(searchResult: SearchResult) { - val searchData = Intent() searchData.putExtra("index", searchResult.chapterIndex) searchData.putExtra("contentPosition", searchResult.contentPosition) 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 8ce74e1d4..7906ad7b3 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 @@ -5,16 +5,21 @@ import android.app.Application import io.legado.app.App import io.legado.app.base.BaseViewModel import io.legado.app.data.entities.Book +import io.legado.app.help.ContentProcessor class SearchContentViewModel(application: Application) : BaseViewModel(application) { var bookUrl: String = "" var book: Book? = null + var contentProcessor: ContentProcessor? = null var lastQuery: String = "" fun initBook(bookUrl: String, success: () -> Unit) { this.bookUrl = bookUrl execute { book = App.db.bookDao().getBook(bookUrl) + book?.let { + contentProcessor = ContentProcessor(it.name, it.origin) + } }.onSuccess { success.invoke() } diff --git a/app/src/main/java/io/legado/app/ui/replace/ReplaceRuleActivity.kt b/app/src/main/java/io/legado/app/ui/replace/ReplaceRuleActivity.kt index 1507f163e..450ae2527 100644 --- a/app/src/main/java/io/legado/app/ui/replace/ReplaceRuleActivity.kt +++ b/app/src/main/java/io/legado/app/ui/replace/ReplaceRuleActivity.kt @@ -19,12 +19,12 @@ import io.legado.app.R import io.legado.app.base.VMBaseActivity import io.legado.app.constant.AppPattern import io.legado.app.data.entities.ReplaceRule -import io.legado.app.help.BookHelp import io.legado.app.help.IntentDataHelp import io.legado.app.help.coroutine.Coroutine import io.legado.app.lib.dialogs.alert import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.primaryTextColor +import io.legado.app.service.help.ReadBook import io.legado.app.ui.association.ImportReplaceRuleActivity import io.legado.app.ui.filepicker.FilePicker import io.legado.app.ui.filepicker.FilePickerDialog @@ -277,7 +277,7 @@ class ReplaceRuleActivity : override fun onDestroy() { super.onDestroy() - Coroutine.async { BookHelp.upReplaceRules() } + Coroutine.async { ReadBook.contentProcessor?.upReplaceRules() } } override fun upCountView() {