diff --git a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt index c37fb3761..9aa30235b 100644 --- a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt +++ b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt @@ -22,7 +22,6 @@ import org.apache.commons.text.StringEscapeUtils import org.jetbrains.anko.share import org.jetbrains.anko.toast import org.jsoup.Jsoup -import org.jsoup.safety.Whitelist class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_read), @@ -253,11 +252,8 @@ class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_r web_view.settings.javaScriptEnabled = true web_view.evaluateJavascript("document.documentElement.outerHTML") { val html = StringEscapeUtils.unescapeJson(it) - val text = Jsoup.clean(html, Whitelist.none()) - .replace(Regex("""&\w+;"""), "") - .trim()//朗读过程中总是听到一些杂音,清理一下 - //longToast(需读内容)调试一下 - viewModel.readAloud(text) + Jsoup.parse(html).text() + viewModel.readAloud(Jsoup.parse(html).textArray()) } } } diff --git a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt index d2aaad80f..fd721e09b 100644 --- a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt @@ -37,7 +37,7 @@ class ReadRssViewModel(application: Application) : BaseViewModel(application), var star = false var textToSpeech: TextToSpeech? = null private var ttsInitFinish = false - private var ttsText = "" + private var ttsTextList = arrayListOf() fun initData(intent: Intent) { execute { @@ -163,13 +163,14 @@ class ReadRssViewModel(application: Application) : BaseViewModel(application), private fun play() { if (!ttsInitFinish) return textToSpeech?.stop() - ttsText.split("\n", " ", "  ").forEach { + ttsTextList.forEach { textToSpeech?.speak(it, TextToSpeech.QUEUE_ADD, null, "rss") } } - fun readAloud(text: String) { - ttsText = text + fun readAloud(textArray: Array) { + ttsTextList.clear() + ttsTextList.addAll(textArray) textToSpeech?.let { play() } ?: let { diff --git a/app/src/main/java/io/legado/app/utils/JsoupExtensions.kt b/app/src/main/java/io/legado/app/utils/JsoupExtensions.kt new file mode 100644 index 000000000..b40cdf391 --- /dev/null +++ b/app/src/main/java/io/legado/app/utils/JsoupExtensions.kt @@ -0,0 +1,69 @@ +package io.legado.app.utils + +import org.jsoup.internal.StringUtil +import org.jsoup.nodes.CDataNode +import org.jsoup.nodes.Element +import org.jsoup.nodes.Node +import org.jsoup.nodes.TextNode +import org.jsoup.select.NodeTraversor +import org.jsoup.select.NodeVisitor + + +fun Element.textArray(): Array { + val accum = StringUtil.borrowBuilder() + NodeTraversor.traverse(object : NodeVisitor { + override fun head(node: Node, depth: Int) { + if (node is TextNode) { + appendNormalisedText(accum, node) + } else if (node is Element) { + if (accum.isNotEmpty() && + (node.isBlock || node.tag().name == "br") && + !lastCharIsWhitespace(accum) + ) accum.append("\n") + } + } + + override fun tail(node: Node, depth: Int) { + // make sure there is a space between block tags and immediately following text nodes
One
Two should be "One Two". + if (node is Element) { + if (node.isBlock && node.nextSibling() is TextNode && !lastCharIsWhitespace( + accum + ) + ) accum.append("\n") + } + } + }, this) + val text = StringUtil.releaseBuilder(accum).trim { it <= ' ' } + return text.splitNotBlank("\n") +} + +private fun appendNormalisedText( + accum: StringBuilder, + textNode: TextNode +) { + val text = textNode.wholeText + if (preserveWhitespace(textNode.parentNode()) || textNode is CDataNode) accum.append(text) else StringUtil.appendNormalisedWhitespace( + accum, + text, + lastCharIsWhitespace(accum) + ) +} + +private fun preserveWhitespace(node: Node?): Boolean { + // looks only at this element and five levels up, to prevent recursion & needless stack searches + if (node is Element) { + var el = node as Element? + var i = 0 + do { + if (el!!.tag().preserveWhitespace()) return true + el = el.parent() + i++ + } while (i < 6 && el != null) + } + return false +} + +private fun lastCharIsWhitespace(sb: java.lang.StringBuilder): Boolean { + return sb.isNotEmpty() && sb[sb.length - 1] == ' ' +} +