diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index ed4c5904d..70bf0a2c6 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -3,13 +3,15 @@ * 关注合作公众号 **[小说拾遗]()** 获取好看的小说。 * 旧版数据导入教程:先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。 -**2020/11/13** +**2020/11/15** * 正文规则添加字体规则,返回ByteArray * js添加方法: ``` base64DecodeToByteArray(str: String?): ByteArray? base64DecodeToByteArray(str: String?, flags: Int): ByteArray? ``` +* 导出添加替换净化 +* 修复正文内容TalkBack不对的bug,优化视障使用体验 **2020/11/08** * 优化书源,订阅源导入,添加保持原名选项 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/data/entities/BookChapter.kt b/app/src/main/java/io/legado/app/data/entities/BookChapter.kt index 3441a4ed4..1668ef19c 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 @@ -5,6 +5,7 @@ import androidx.room.Entity import androidx.room.ForeignKey import androidx.room.Ignore import androidx.room.Index +import io.legado.app.model.analyzeRule.AnalyzeUrl import io.legado.app.utils.GSON import io.legado.app.utils.MD5Utils import io.legado.app.utils.NetworkUtils @@ -61,7 +62,12 @@ data class BookChapter( } fun getAbsoluteURL(): String { - return NetworkUtils.getAbsoluteURL(baseUrl, url)!! + val urlArray = url.split(AnalyzeUrl.splitUrlRegex) + var absoluteUrl = NetworkUtils.getAbsoluteURL(baseUrl, urlArray[0])!! + if (urlArray.size > 1) { + absoluteUrl += urlArray[1] + } + return absoluteUrl } fun getFileName(): String = String.format("%05d-%s.nb", index, MD5Utils.md5Encode16(title)) 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..1bc3a5bcb --- /dev/null +++ b/app/src/main/java/io/legado/app/help/ContentProcessor.kt @@ -0,0 +1,79 @@ +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 -> + if (item.pattern.isNotEmpty()) { + try { + content1 = if (item.isRegex) { + content1.replace(item.pattern.toRegex(), item.replacement) + } else { + content1.replace(item.pattern, 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/model/analyzeRule/AnalyzeUrl.kt b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt index 60ef3b8d6..16d9f3d29 100644 --- a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt +++ b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt @@ -45,7 +45,7 @@ class AnalyzeUrl( headerMapF: Map? = null ) : JsExtensions { companion object { - private val splitUrlRegex = Regex(",\\s*(?=\\{)") + val splitUrlRegex = Regex(",\\s*(?=\\{)") private val pagePattern = Pattern.compile("<(.*?)>") private val jsonType = MediaType.parse("application/json; charset=utf-8") } diff --git a/app/src/main/java/io/legado/app/model/webBook/WebBook.kt b/app/src/main/java/io/legado/app/model/webBook/WebBook.kt index 9e69cff3d..c9923b776 100644 --- a/app/src/main/java/io/legado/app/model/webBook/WebBook.kt +++ b/app/src/main/java/io/legado/app/model/webBook/WebBook.kt @@ -144,9 +144,10 @@ class WebBook(val bookSource: BookSource) { ).getResponseAwait(bookSource.bookSourceUrl) BookChapterList.analyzeChapterList( this, - book, res.body, + book, + res.body, bookSource, - res.url + book.tocUrl ) } @@ -211,7 +212,7 @@ class WebBook(val bookSource: BookSource) { book, bookChapter, bookSource, - res.url, + bookChapter.getAbsoluteURL(), nextChapterUrl ) } 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/read/page/ContentTextView.kt b/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt index 86dc57d52..6bc543a0b 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt @@ -47,13 +47,11 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at init { callBack = activity as CallBack - contentDescription = textPage.text } fun setContent(pageData: PageData) { this.textChapter = pageData.textChapter this.textPage = pageData.textPage - contentDescription = textPage.text invalidate() } @@ -193,11 +191,13 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at textPage = pageFactory.curData.textPage pageOffset -= textPage.height.toInt() upView?.invoke(textPage) + contentDescription = textPage.text } else if (pageOffset < -textPage.height) { pageOffset += textPage.height.toInt() pageFactory.moveToNext(false) textPage = pageFactory.curData.textPage upView?.invoke(textPage) + contentDescription = textPage.text } invalidate() } diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/ContentView.kt b/app/src/main/java/io/legado/app/ui/book/read/page/ContentView.kt index d94c30086..85e2d9d80 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/ContentView.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/ContentView.kt @@ -226,6 +226,10 @@ class ContentView(context: Context) : FrameLayout(context) { content_text_view.setContent(pageData) } + fun setContentDescription(content: String) { + content_text_view.contentDescription = content + } + fun resetPageOffset() { content_text_view.resetPageOffset() } diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt b/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt index 9846bee77..0fb334989 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt @@ -72,19 +72,18 @@ class PageView(context: Context, attrs: AttributeSet) : private var firstCharIndex: Int = 0 val slopSquare by lazy { ViewConfiguration.get(context).scaledTouchSlop } - private val topLeftRectF = RectF(0F, 0F, width * 0.33f, height * 0.33f) - private val topCenterRectF = RectF(width * 0.33f, 0F, width * 0.66f, height * 0.33f) - private val topRightRectF = RectF(width * 0.36f, 0F, width.toFloat(), height * 0.33f) - private val middleLeftRectF = RectF(0F, height * 0.33f, width * 0.33f, height * 0.66f) - private val centerRectF = RectF(width * 0.33f, height * 0.33f, width * 0.66f, height * 0.66f) - private val middleRightRectF = - RectF(width * 0.66f, height * 0.33f, width.toFloat(), height * 0.66f) - private val bottomLeftRectF = RectF(0F, height * 0.66f, width * 0.33f, height.toFloat()) - private val bottomCenterRectF = - RectF(width * 0.33f, height * 0.66f, width * 0.66f, height.toFloat()) - private val bottomRightRectF = - RectF(width * 0.66f, height * 0.66f, width.toFloat(), height.toFloat()) - private val autoPageRect by lazy { Rect() } + private val tlRect = RectF(10F, 10F, width * 0.33f, height * 0.33f) + private val tcRect = RectF(width * 0.33f, 10F, width * 0.66f, height * 0.33f) + private val trRect = RectF(width * 0.36f, 10F, width - 10f, height * 0.33f) + private val mlRect = RectF(10F, height * 0.33f, width * 0.33f, height * 0.66f) + private val mcRect = RectF(width * 0.33f, height * 0.33f, width * 0.66f, height * 0.66f) + private val mrRect = RectF(width * 0.66f, height * 0.33f, width - 10f, height * 0.66f) + private val blRect = RectF(10F, height * 0.66f, width * 0.33f, height - 10f) + private val bcRect = RectF(width * 0.33f, height * 0.66f, width * 0.66f, height - 10f) + private val brRect = RectF(width * 0.66f, height * 0.66f, width - 10f, height - 10f) + private val autoPageRect by lazy { + Rect() + } private val autoPagePint by lazy { Paint().apply { color = context.accentColor @@ -102,15 +101,15 @@ class PageView(context: Context, attrs: AttributeSet) : override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { super.onSizeChanged(w, h, oldw, oldh) - topLeftRectF.set(0F, 0F, width * 0.33f, height * 0.33f) - topCenterRectF.set(width * 0.33f, 0F, width * 0.66f, height * 0.33f) - topRightRectF.set(width * 0.36f, 0F, width.toFloat(), height * 0.33f) - middleLeftRectF.set(0F, height * 0.33f, width * 0.33f, height * 0.66f) - centerRectF.set(width * 0.33f, height * 0.33f, width * 0.66f, height * 0.66f) - middleRightRectF.set(width * 0.66f, height * 0.33f, width.toFloat(), height * 0.66f) - bottomLeftRectF.set(0F, height * 0.66f, width * 0.33f, height.toFloat()) - bottomCenterRectF.set(width * 0.33f, height * 0.66f, width * 0.66f, height.toFloat()) - bottomRightRectF.set(width * 0.66f, height * 0.66f, width.toFloat(), height.toFloat()) + tlRect.set(10F, 10F, width * 0.33f, height * 0.33f) + tcRect.set(width * 0.33f, 10F, width * 0.66f, height * 0.33f) + trRect.set(width * 0.36f, 10F, width - 10f, height * 0.33f) + mlRect.set(10F, height * 0.33f, width * 0.33f, height * 0.66f) + mcRect.set(width * 0.33f, height * 0.33f, width * 0.66f, height * 0.66f) + mrRect.set(width * 0.66f, height * 0.33f, width - 10f, height * 0.66f) + blRect.set(10F, height * 0.66f, width * 0.33f, height - 10f) + bcRect.set(width * 0.33f, height * 0.66f, width * 0.66f, height - 10f) + brRect.set(width * 0.66f, height * 0.66f, width - 10f, height - 10f) prevPage.x = -w.toFloat() pageDelegate?.setViewSize(w, h) if (oldw != 0 && oldh != 0) { @@ -258,38 +257,37 @@ class PageView(context: Context, attrs: AttributeSet) : /** * 单击 */ - private fun onSingleTapUp(): Boolean { + private fun onSingleTapUp() { when { isTextSelected -> isTextSelected = false - centerRectF.contains(startX, startY) -> if (!isAbortAnim) { + mcRect.contains(startX, startY) -> if (!isAbortAnim) { click(AppConfig.clickActionMiddleCenter) } - bottomCenterRectF.contains(startX, startY) -> { + bcRect.contains(startX, startY) -> { click(AppConfig.clickActionBottomCenter) } - bottomLeftRectF.contains(startX, startY) -> { + blRect.contains(startX, startY) -> { click(AppConfig.clickActionBottomLeft) } - bottomRightRectF.contains(startX, startY) -> { + brRect.contains(startX, startY) -> { click(AppConfig.clickActionBottomRight) } - middleLeftRectF.contains(startX, startY) -> { + mlRect.contains(startX, startY) -> { click(AppConfig.clickActionMiddleLeft) } - middleRightRectF.contains(startX, startY) -> { + mrRect.contains(startX, startY) -> { click(AppConfig.clickActionMiddleRight) } - topLeftRectF.contains(startX, startY) -> { + tlRect.contains(startX, startY) -> { click(AppConfig.clickActionTopLeft) } - topCenterRectF.contains(startX, startY) -> { + tcRect.contains(startX, startY) -> { click(AppConfig.clickActionTopCenter) } - topRightRectF.contains(startX, startY) -> { + trRect.contains(startX, startY) -> { click(AppConfig.clickActionTopRight) } } - return true } private fun click(action: Int) { @@ -375,6 +373,7 @@ class PageView(context: Context, attrs: AttributeSet) : } override fun upContent(relativePosition: Int, resetPageOffset: Boolean) { + curPage.setContentDescription(pageFactory.curData.textPage.text) if (isScroll && !callBack.isAutoPage) { curPage.setContent(pageFactory.curData, resetPageOffset) } else { 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/main/MainViewModel.kt b/app/src/main/java/io/legado/app/ui/main/MainViewModel.kt index a87ea2e32..0f447bf30 100644 --- a/app/src/main/java/io/legado/app/ui/main/MainViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/main/MainViewModel.kt @@ -68,44 +68,43 @@ class MainViewModel(application: Application) : BaseViewModel(application) { } } + @Synchronized private fun updateToc() { - synchronized(this) { - bookMap.forEach { bookEntry -> - if (!updateList.contains(bookEntry.key)) { - val book = bookEntry.value - synchronized(this) { - updateList.add(book.bookUrl) - postEvent(EventBus.UP_BOOK, book.bookUrl) - } - App.db.bookSourceDao().getBookSource(book.origin)?.let { bookSource -> - val webBook = WebBook(bookSource) - webBook.getChapterList(book, context = upTocPool) - .timeout(300000) - .onSuccess(IO) { - App.db.bookDao().update(book) - App.db.bookChapterDao().delByBook(book.bookUrl) - App.db.bookChapterDao().insert(*it.toTypedArray()) - cacheBook(webBook, book) - } - .onError { - it.printStackTrace() - } - .onFinally { - synchronized(this) { - bookMap.remove(bookEntry.key) - updateList.remove(book.bookUrl) - postEvent(EventBus.UP_BOOK, book.bookUrl) - upNext() - } + bookMap.forEach { bookEntry -> + if (!updateList.contains(bookEntry.key)) { + val book = bookEntry.value + synchronized(this) { + updateList.add(book.bookUrl) + postEvent(EventBus.UP_BOOK, book.bookUrl) + } + App.db.bookSourceDao().getBookSource(book.origin)?.let { bookSource -> + val webBook = WebBook(bookSource) + webBook.getChapterList(book, context = upTocPool) + .timeout(300000) + .onSuccess(IO) { + App.db.bookDao().update(book) + App.db.bookChapterDao().delByBook(book.bookUrl) + App.db.bookChapterDao().insert(*it.toTypedArray()) + cacheBook(webBook, book) + } + .onError { + it.printStackTrace() + } + .onFinally { + synchronized(this) { + bookMap.remove(bookEntry.key) + updateList.remove(book.bookUrl) + postEvent(EventBus.UP_BOOK, book.bookUrl) + upNext() } - } ?: synchronized(this) { - bookMap.remove(bookEntry.key) - updateList.remove(book.bookUrl) - postEvent(EventBus.UP_BOOK, book.bookUrl) - upNext() - } - return + } + } ?: synchronized(this) { + bookMap.remove(bookEntry.key) + updateList.remove(book.bookUrl) + postEvent(EventBus.UP_BOOK, book.bookUrl) + upNext() } + return } } } 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() { diff --git a/app/src/main/java/io/legado/app/ui/widget/image/CircleImageView.kt b/app/src/main/java/io/legado/app/ui/widget/image/CircleImageView.kt index f5781183e..acde8db7a 100644 --- a/app/src/main/java/io/legado/app/ui/widget/image/CircleImageView.kt +++ b/app/src/main/java/io/legado/app/ui/widget/image/CircleImageView.kt @@ -139,6 +139,7 @@ class CircleImageView @JvmOverloads constructor( DEFAULT_CIRCLE_BACKGROUND_COLOR ) text = a.getString(R.styleable.CircleImageView_text) + contentDescription = text if (a.hasValue(R.styleable.CircleImageView_textColor)) { textColor = a.getColor( R.styleable.CircleImageView_textColor, @@ -226,6 +227,7 @@ class CircleImageView @JvmOverloads constructor( fun setText(text: String?) { this.text = text + contentDescription = text invalidate() } diff --git a/app/src/main/java/io/legado/app/utils/NetworkUtils.kt b/app/src/main/java/io/legado/app/utils/NetworkUtils.kt index bc4436394..d08e4a450 100644 --- a/app/src/main/java/io/legado/app/utils/NetworkUtils.kt +++ b/app/src/main/java/io/legado/app/utils/NetworkUtils.kt @@ -10,6 +10,7 @@ import java.util.regex.Pattern @Suppress("unused", "MemberVisibilityCanBePrivate") object NetworkUtils { + fun getUrl(response: Response<*>): String { response.raw().networkResponse()?.let { return it.request().url().toString() @@ -79,6 +80,7 @@ object NetworkUtils { fun getAbsoluteURL(baseURL: String?, relativePath: String?): String? { if (baseURL.isNullOrEmpty()) return relativePath if (relativePath.isNullOrEmpty()) return baseURL + if (relativePath.isAbsUrl()) return baseURL var relativeUrl = relativePath try { val absoluteUrl = URL(baseURL.substringBefore(",")) 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 f00c332d5..f70ab447e 100644 --- a/app/src/main/java/io/legado/app/utils/StringExtensions.kt +++ b/app/src/main/java/io/legado/app/utils/StringExtensions.kt @@ -23,8 +23,7 @@ fun String.parseToUri(): Uri { fun String?.isAbsUrl() = this?.let { - it.startsWith("http://", true) - || it.startsWith("https://", true) + it.startsWith("http://", true) || it.startsWith("https://", true) } ?: false fun String?.isJson(): Boolean = diff --git a/app/src/main/res/layout/view_read_menu.xml b/app/src/main/res/layout/view_read_menu.xml index d48221169..27f387599 100644 --- a/app/src/main/res/layout/view_read_menu.xml +++ b/app/src/main/res/layout/view_read_menu.xml @@ -10,6 +10,7 @@ android:id="@+id/vw_menu_bg" android:layout_width="match_parent" android:layout_height="match_parent" + android:contentDescription="@string/content" tools:layout_editor_absoluteX="0dp" tools:layout_editor_absoluteY="0dp" /> @@ -172,19 +173,22 @@ + android:layout_height="100dp" + android:importantForAccessibility="no"> + android:layout_height="match_parent" + android:importantForAccessibility="no" /> + android:orientation="vertical" + android:importantForAccessibility="no"> + android:orientation="horizontal" + android:importantForAccessibility="no"> + android:orientation="horizontal" + android:importantForAccessibility="no"> + android:layout_weight="1" + android:importantForAccessibility="no" /> + android:layout_weight="2" + android:importantForAccessibility="no" /> + android:layout_weight="2" + android:importantForAccessibility="no" /> + + android:layout_weight="2" + android:importantForAccessibility="no" /> + + android:layout_weight="1" + android:importantForAccessibility="no" /> +