优化
This commit is contained in:
@@ -17,10 +17,14 @@ class TTS {
|
|||||||
|
|
||||||
private val clearTtsRunnable = Runnable { clearTts() }
|
private val clearTtsRunnable = Runnable { clearTts() }
|
||||||
|
|
||||||
|
private var speakStateListener: SpeakStateListener? = null
|
||||||
|
|
||||||
private var textToSpeech: TextToSpeech? = null
|
private var textToSpeech: TextToSpeech? = null
|
||||||
|
|
||||||
private var text: String? = null
|
private var text: String? = null
|
||||||
|
|
||||||
|
private var onInit = false
|
||||||
|
|
||||||
private val initListener by lazy {
|
private val initListener by lazy {
|
||||||
InitListener()
|
InitListener()
|
||||||
}
|
}
|
||||||
@@ -29,17 +33,40 @@ class TTS {
|
|||||||
TTSUtteranceListener()
|
TTSUtteranceListener()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val isSpeaking: Boolean
|
||||||
|
get() {
|
||||||
|
return textToSpeech?.isSpeaking ?: false
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
fun setSpeakStateListener(speakStateListener: SpeakStateListener) {
|
||||||
|
this.speakStateListener = speakStateListener
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
fun removeSpeakStateListener() {
|
||||||
|
speakStateListener = null
|
||||||
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun speak(text: String) {
|
fun speak(text: String) {
|
||||||
handler.removeCallbacks(clearTtsRunnable)
|
handler.removeCallbacks(clearTtsRunnable)
|
||||||
this.text = text
|
this.text = text
|
||||||
|
if (onInit) {
|
||||||
|
return
|
||||||
|
}
|
||||||
if (textToSpeech == null) {
|
if (textToSpeech == null) {
|
||||||
|
onInit = true
|
||||||
textToSpeech = TextToSpeech(appCtx, initListener)
|
textToSpeech = TextToSpeech(appCtx, initListener)
|
||||||
} else {
|
} else {
|
||||||
addTextToSpeakList()
|
addTextToSpeakList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun stop() {
|
||||||
|
textToSpeech?.stop()
|
||||||
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun clearTts() {
|
fun clearTts() {
|
||||||
textToSpeech?.let { tts ->
|
textToSpeech?.let { tts ->
|
||||||
@@ -80,6 +107,7 @@ class TTS {
|
|||||||
} else {
|
} else {
|
||||||
appCtx.toastOnUi(R.string.tts_init_failed)
|
appCtx.toastOnUi(R.string.tts_init_failed)
|
||||||
}
|
}
|
||||||
|
onInit = false
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -92,11 +120,13 @@ class TTS {
|
|||||||
override fun onStart(utteranceId: String?) {
|
override fun onStart(utteranceId: String?) {
|
||||||
//开始朗读取消释放资源任务
|
//开始朗读取消释放资源任务
|
||||||
handler.removeCallbacks(clearTtsRunnable)
|
handler.removeCallbacks(clearTtsRunnable)
|
||||||
|
speakStateListener?.onStart()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDone(utteranceId: String?) {
|
override fun onDone(utteranceId: String?) {
|
||||||
//一分钟没有朗读释放资源
|
//一分钟没有朗读释放资源
|
||||||
handler.postDelayed(clearTtsRunnable, 60000L)
|
handler.postDelayed(clearTtsRunnable, 60000L)
|
||||||
|
speakStateListener?.onDone()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated("Deprecated in Java")
|
@Deprecated("Deprecated in Java")
|
||||||
@@ -105,4 +135,9 @@ class TTS {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface SpeakStateListener {
|
||||||
|
fun onStart()
|
||||||
|
fun onDone()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -267,16 +267,15 @@ class ReadRssActivity : VMBaseActivity<ActivityRssReadBinding, ReadRssViewModel>
|
|||||||
|
|
||||||
@SuppressLint("SetJavaScriptEnabled")
|
@SuppressLint("SetJavaScriptEnabled")
|
||||||
private fun readAloud() {
|
private fun readAloud() {
|
||||||
if (viewModel.textToSpeech?.isSpeaking == true) {
|
if (viewModel.tts?.isSpeaking == true) {
|
||||||
viewModel.textToSpeech?.stop()
|
viewModel.tts?.stop()
|
||||||
upTtsMenu(false)
|
upTtsMenu(false)
|
||||||
} else {
|
} else {
|
||||||
binding.webView.settings.javaScriptEnabled = true
|
binding.webView.settings.javaScriptEnabled = true
|
||||||
binding.webView.evaluateJavascript("document.documentElement.outerHTML") {
|
binding.webView.evaluateJavascript("document.documentElement.outerHTML") {
|
||||||
val html = StringEscapeUtils.unescapeJson(it)
|
val html = StringEscapeUtils.unescapeJson(it)
|
||||||
.replace("^\"|\"$".toRegex(), "")
|
.replace("^\"|\"$".toRegex(), "")
|
||||||
Jsoup.parse(html).text()
|
viewModel.readAloud(Jsoup.parse(html).textArray().joinToString("\n"))
|
||||||
viewModel.readAloud(Jsoup.parse(html).textArray())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,20 +3,18 @@ package io.legado.app.ui.rss.read
|
|||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.speech.tts.TextToSpeech
|
|
||||||
import android.speech.tts.UtteranceProgressListener
|
|
||||||
import android.util.Base64
|
import android.util.Base64
|
||||||
import android.webkit.URLUtil
|
import android.webkit.URLUtil
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import io.legado.app.R
|
|
||||||
import io.legado.app.base.BaseViewModel
|
import io.legado.app.base.BaseViewModel
|
||||||
import io.legado.app.constant.AppConst
|
import io.legado.app.constant.AppConst
|
||||||
import io.legado.app.data.appDb
|
import io.legado.app.data.appDb
|
||||||
import io.legado.app.data.entities.RssArticle
|
import io.legado.app.data.entities.RssArticle
|
||||||
import io.legado.app.data.entities.RssSource
|
import io.legado.app.data.entities.RssSource
|
||||||
import io.legado.app.data.entities.RssStar
|
import io.legado.app.data.entities.RssStar
|
||||||
|
import io.legado.app.help.TTS
|
||||||
import io.legado.app.help.http.newCallResponseBody
|
import io.legado.app.help.http.newCallResponseBody
|
||||||
import io.legado.app.help.http.okHttpClient
|
import io.legado.app.help.http.okHttpClient
|
||||||
import io.legado.app.model.analyzeRule.AnalyzeUrl
|
import io.legado.app.model.analyzeRule.AnalyzeUrl
|
||||||
@@ -27,17 +25,14 @@ import java.io.File
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
class ReadRssViewModel(application: Application) : BaseViewModel(application),
|
class ReadRssViewModel(application: Application) : BaseViewModel(application) {
|
||||||
TextToSpeech.OnInitListener {
|
|
||||||
var callBack: CallBack? = null
|
var callBack: CallBack? = null
|
||||||
var rssSource: RssSource? = null
|
var rssSource: RssSource? = null
|
||||||
var rssArticle: RssArticle? = null
|
var rssArticle: RssArticle? = null
|
||||||
|
var tts: TTS? = null
|
||||||
val contentLiveData = MutableLiveData<String>()
|
val contentLiveData = MutableLiveData<String>()
|
||||||
val urlLiveData = MutableLiveData<AnalyzeUrl>()
|
val urlLiveData = MutableLiveData<AnalyzeUrl>()
|
||||||
var rssStar: RssStar? = null
|
var rssStar: RssStar? = null
|
||||||
var textToSpeech: TextToSpeech? = null
|
|
||||||
private var ttsInitFinish = false
|
|
||||||
private var ttsTextList = arrayListOf<String>()
|
|
||||||
|
|
||||||
fun initData(intent: Intent) {
|
fun initData(intent: Intent) {
|
||||||
execute {
|
execute {
|
||||||
@@ -192,59 +187,26 @@ class ReadRssViewModel(application: Application) : BaseViewModel(application),
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
override fun onInit(status: Int) {
|
fun readAloud(text: String) {
|
||||||
if (status == TextToSpeech.SUCCESS) {
|
if (tts == null) {
|
||||||
textToSpeech?.setOnUtteranceProgressListener(TTSUtteranceListener())
|
tts = TTS().apply {
|
||||||
ttsInitFinish = true
|
setSpeakStateListener(object : TTS.SpeakStateListener {
|
||||||
play()
|
override fun onStart() {
|
||||||
} else {
|
callBack?.upTtsMenu(true)
|
||||||
context.toastOnUi(R.string.tts_init_failed)
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Synchronized
|
override fun onDone() {
|
||||||
private fun play() {
|
callBack?.upTtsMenu(false)
|
||||||
if (!ttsInitFinish) return
|
}
|
||||||
textToSpeech?.stop()
|
})
|
||||||
ttsTextList.forEach {
|
}
|
||||||
textToSpeech?.speak(it, TextToSpeech.QUEUE_ADD, null, "rss")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun readAloud(textArray: Array<String>) {
|
|
||||||
ttsTextList.clear()
|
|
||||||
ttsTextList.addAll(textArray)
|
|
||||||
textToSpeech?.let {
|
|
||||||
play()
|
|
||||||
} ?: let {
|
|
||||||
textToSpeech = TextToSpeech(context, this)
|
|
||||||
}
|
}
|
||||||
|
tts?.speak(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
super.onCleared()
|
super.onCleared()
|
||||||
textToSpeech?.stop()
|
tts?.clearTts()
|
||||||
textToSpeech?.shutdown()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 朗读监听
|
|
||||||
*/
|
|
||||||
private inner class TTSUtteranceListener : UtteranceProgressListener() {
|
|
||||||
|
|
||||||
override fun onStart(s: String) {
|
|
||||||
callBack?.upTtsMenu(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDone(s: String) {
|
|
||||||
callBack?.upTtsMenu(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated("Deprecated in Java")
|
|
||||||
override fun onError(s: String) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CallBack {
|
interface CallBack {
|
||||||
|
|||||||
Reference in New Issue
Block a user