From b1f019ffa7e9a2b2c62ebf946f7ea8aa8b700232 Mon Sep 17 00:00:00 2001 From: kunfei Date: Fri, 13 Mar 2020 17:09:28 +0800 Subject: [PATCH 1/7] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/ui/book/local/ImportBookActivity.kt | 37 +++++++++---------- .../legado/app/ui/filechooser/FilePicker.kt | 12 +++--- 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/book/local/ImportBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/local/ImportBookActivity.kt index ecd6f6e8f..3ce47ee99 100644 --- a/app/src/main/java/io/legado/app/ui/book/local/ImportBookActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/local/ImportBookActivity.kt @@ -17,9 +17,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import io.legado.app.App import io.legado.app.R import io.legado.app.base.VMBaseActivity -import io.legado.app.constant.EventBus import io.legado.app.help.AppConfig -import io.legado.app.help.ReadBookConfig import io.legado.app.help.permission.Permissions import io.legado.app.help.permission.PermissionsCompat import io.legado.app.ui.filechooser.FileChooserDialog @@ -86,23 +84,6 @@ class ImportBookActivity : VMBaseActivity(R.layout.activity } }) - // 没有权限就显示一个授权提示和按钮 - if (PermissionsCompat.check(this, *Permissions.Group.STORAGE)) { - hint_per.visibility = View.GONE - } else { - hint_per.visibility = View.VISIBLE - tv_request_per.onClick { - PermissionsCompat.Builder(this) - .addPermissions(*Permissions.Group.STORAGE) - .rationale(R.string.tip_perm_request_storage) - .onGranted { - hint_per.visibility = View.GONE - initData() - upRootDoc() - } - .request() - } - } } private fun initEvent() { @@ -147,6 +128,24 @@ class ImportBookActivity : VMBaseActivity(R.layout.activity subDocs.clear() path = it } + } ?: let { + // 没有权限就显示一个授权提示和按钮 + if (PermissionsCompat.check(this, *Permissions.Group.STORAGE)) { + hint_per.visibility = View.GONE + } else { + hint_per.visibility = View.VISIBLE + tv_request_per.onClick { + PermissionsCompat.Builder(this) + .addPermissions(*Permissions.Group.STORAGE) + .rationale(R.string.tip_perm_request_storage) + .onGranted { + hint_per.visibility = View.GONE + initData() + upRootDoc() + } + .request() + } + } } upPath() } diff --git a/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt b/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt index 8f5549709..717ad8f11 100644 --- a/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt +++ b/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt @@ -31,7 +31,7 @@ object FilePicker { 0 -> default?.invoke() 1 -> { try { - val intent = getSelectDirIntent() + val intent = createSelectDirIntent() activity.startActivityForResult(intent, requestCode) } catch (e: java.lang.Exception) { e.printStackTrace() @@ -68,7 +68,7 @@ object FilePicker { 0 -> default?.invoke() 1 -> { try { - val intent = getSelectDirIntent() + val intent = createSelectDirIntent() fragment.startActivityForResult(intent, requestCode) } catch (e: java.lang.Exception) { e.printStackTrace() @@ -106,7 +106,7 @@ object FilePicker { 0 -> default?.invoke() 1 -> { try { - val intent = getSelectFileIntent() + val intent = createSelectFileIntent() intent.type = type//设置类型 activity.startActivityForResult(intent, requestCode) } catch (e: java.lang.Exception) { @@ -147,7 +147,7 @@ object FilePicker { 0 -> default?.invoke() 1 -> { try { - val intent = getSelectFileIntent() + val intent = createSelectFileIntent() intent.type = type//设置类型 fragment.startActivityForResult(intent, requestCode) } catch (e: java.lang.Exception) { @@ -168,14 +168,14 @@ object FilePicker { }.show() } - private fun getSelectFileIntent(): Intent { + private fun createSelectFileIntent(): Intent { val intent = Intent(Intent.ACTION_GET_CONTENT) intent.addCategory(Intent.CATEGORY_OPENABLE) intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) return intent } - private fun getSelectDirIntent(): Intent { + private fun createSelectDirIntent(): Intent { val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) return intent From 8cd18581a8136b394402995de2847ea0f46545ec Mon Sep 17 00:00:00 2001 From: kunfei Date: Fri, 13 Mar 2020 18:24:48 +0800 Subject: [PATCH 2/7] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../legado/app/service/TTSReadAloudService.kt | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/io/legado/app/service/TTSReadAloudService.kt b/app/src/main/java/io/legado/app/service/TTSReadAloudService.kt index 61507bf7c..c7fc02b08 100644 --- a/app/src/main/java/io/legado/app/service/TTSReadAloudService.kt +++ b/app/src/main/java/io/legado/app/service/TTSReadAloudService.kt @@ -42,20 +42,21 @@ class TTSReadAloudService : BaseReadAloudService(), TextToSpeech.OnInitListener clearTTS() } + @Synchronized override fun onInit(status: Int) { - launch { - if (status == TextToSpeech.SUCCESS) { - textToSpeech?.language = Locale.CHINA - textToSpeech?.setOnUtteranceProgressListener(TTSUtteranceListener()) - ttsIsSuccess = true - play() - } else { + if (status == TextToSpeech.SUCCESS) { + textToSpeech?.language = Locale.CHINA + textToSpeech?.setOnUtteranceProgressListener(TTSUtteranceListener()) + ttsIsSuccess = true + play() + } else { + launch { toast(R.string.tts_init_failed) } } } - @Suppress("DEPRECATION") + @Synchronized override fun play() { if (contentList.isEmpty() || !ttsIsSuccess) { return From 8d5a1877aa2d08d206a631a801f73bf78e04c1ff Mon Sep 17 00:00:00 2001 From: kunfei Date: Fri, 13 Mar 2020 18:34:57 +0800 Subject: [PATCH 3/7] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../legado/app/service/TTSReadAloudService.kt | 6 +-- .../app/ui/rss/read/ReadRssViewModel.kt | 38 +++++++++++++------ 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/io/legado/app/service/TTSReadAloudService.kt b/app/src/main/java/io/legado/app/service/TTSReadAloudService.kt index c7fc02b08..1f63dac89 100644 --- a/app/src/main/java/io/legado/app/service/TTSReadAloudService.kt +++ b/app/src/main/java/io/legado/app/service/TTSReadAloudService.kt @@ -29,7 +29,7 @@ class TTSReadAloudService : BaseReadAloudService(), TextToSpeech.OnInitListener } } - private var ttsIsSuccess: Boolean = false + private var ttsInitFinish = false override fun onCreate() { super.onCreate() @@ -47,7 +47,7 @@ class TTSReadAloudService : BaseReadAloudService(), TextToSpeech.OnInitListener if (status == TextToSpeech.SUCCESS) { textToSpeech?.language = Locale.CHINA textToSpeech?.setOnUtteranceProgressListener(TTSUtteranceListener()) - ttsIsSuccess = true + ttsInitFinish = true play() } else { launch { @@ -58,7 +58,7 @@ class TTSReadAloudService : BaseReadAloudService(), TextToSpeech.OnInitListener @Synchronized override fun play() { - if (contentList.isEmpty() || !ttsIsSuccess) { + if (contentList.isEmpty() || !ttsInitFinish) { return } if (requestFocus()) { 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 6d90fd181..d2aaad80f 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 @@ -35,7 +35,9 @@ class ReadRssViewModel(application: Application) : BaseViewModel(application), val contentLiveData = MutableLiveData() val urlLiveData = MutableLiveData() var star = false - var textToSpeech: TextToSpeech = TextToSpeech(context, this) + var textToSpeech: TextToSpeech? = null + private var ttsInitFinish = false + private var ttsText = "" fun initData(intent: Intent) { execute { @@ -143,28 +145,42 @@ class ReadRssViewModel(application: Application) : BaseViewModel(application), } } + @Synchronized override fun onInit(status: Int) { - launch { - if (status == TextToSpeech.SUCCESS) { - textToSpeech.language = Locale.CHINA - textToSpeech.setOnUtteranceProgressListener(TTSUtteranceListener()) - } else { + if (status == TextToSpeech.SUCCESS) { + textToSpeech?.language = Locale.CHINA + textToSpeech?.setOnUtteranceProgressListener(TTSUtteranceListener()) + ttsInitFinish = true + play() + } else { + launch { toast(R.string.tts_init_failed) } } } + @Synchronized + private fun play() { + if (!ttsInitFinish) return + textToSpeech?.stop() + ttsText.split("\n", " ", "  ").forEach { + textToSpeech?.speak(it, TextToSpeech.QUEUE_ADD, null, "rss") + } + } + fun readAloud(text: String) { - textToSpeech.stop() - text.split("\n", " ", "  ").forEach { - textToSpeech.speak(it, TextToSpeech.QUEUE_ADD, null, "rss") + ttsText = text + textToSpeech?.let { + play() + } ?: let { + textToSpeech = TextToSpeech(context, this) } } override fun onCleared() { super.onCleared() - textToSpeech.stop() - textToSpeech.shutdown() + textToSpeech?.stop() + textToSpeech?.shutdown() } /** From d9f7e5333891694c319c85a1827d96a084f0e2eb Mon Sep 17 00:00:00 2001 From: kunfei Date: Fri, 13 Mar 2020 18:36:32 +0800 Subject: [PATCH 4/7] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 79ea599d1..c37fb3761 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 @@ -246,8 +246,8 @@ class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_r @SuppressLint("SetJavaScriptEnabled") private fun readAloud() { - if (viewModel.textToSpeech.isSpeaking) { - viewModel.textToSpeech.stop() + if (viewModel.textToSpeech?.isSpeaking == true) { + viewModel.textToSpeech?.stop() upTtsMenu(false) } else { web_view.settings.javaScriptEnabled = true From 41789f5bc5d697996a0acd7f443e81fd92980186 Mon Sep 17 00:00:00 2001 From: kunfei Date: Fri, 13 Mar 2020 19:49:55 +0800 Subject: [PATCH 5/7] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../legado/app/ui/rss/read/ReadRssActivity.kt | 8 +-- .../app/ui/rss/read/ReadRssViewModel.kt | 9 +-- .../io/legado/app/utils/JsoupExtensions.kt | 69 +++++++++++++++++++ 3 files changed, 76 insertions(+), 10 deletions(-) create mode 100644 app/src/main/java/io/legado/app/utils/JsoupExtensions.kt 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] == ' ' +} + From a14e350591dbf2774b898484c8b449e282619a78 Mon Sep 17 00:00:00 2001 From: kunfei Date: Fri, 13 Mar 2020 19:55:27 +0800 Subject: [PATCH 6/7] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/updateLog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index bab4ef9ab..0d0ff4bc6 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -6,6 +6,7 @@ * 更改书架控件,ViewPager2替换回2.0使用的ViewPager,解决下拉不流畅问题 * 修复点击作者搜索后,打开的详情页还是原来的书籍的bug * 修改朗读菜单 +* 优化rss朗读 **2020/03/12** * 导入本地添加需要权限模式 From 3a81d34a06eecfb11e4acea12128b3b4836de3bb Mon Sep 17 00:00:00 2001 From: kunfei Date: Fri, 13 Mar 2020 19:57:36 +0800 Subject: [PATCH 7/7] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/io/legado/app/utils/JsoupExtensions.kt | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/io/legado/app/utils/JsoupExtensions.kt b/app/src/main/java/io/legado/app/utils/JsoupExtensions.kt index b40cdf391..3f347e4a1 100644 --- a/app/src/main/java/io/legado/app/utils/JsoupExtensions.kt +++ b/app/src/main/java/io/legado/app/utils/JsoupExtensions.kt @@ -24,7 +24,6 @@ fun Element.textArray(): Array { } 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 @@ -37,12 +36,11 @@ fun Element.textArray(): Array { return text.splitNotBlank("\n") } -private fun appendNormalisedText( - accum: StringBuilder, - textNode: TextNode -) { +private fun appendNormalisedText(accum: StringBuilder, textNode: TextNode) { val text = textNode.wholeText - if (preserveWhitespace(textNode.parentNode()) || textNode is CDataNode) accum.append(text) else StringUtil.appendNormalisedWhitespace( + if (preserveWhitespace(textNode.parentNode()) || textNode is CDataNode) + accum.append(text) + else StringUtil.appendNormalisedWhitespace( accum, text, lastCharIsWhitespace(accum) @@ -50,7 +48,6 @@ private fun appendNormalisedText( } 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