pull/2343/head
kunfei 3 years ago
parent f424a18b5b
commit 9706195ae2
  1. 35
      app/src/main/java/io/legado/app/help/TTS.kt
  2. 7
      app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt
  3. 66
      app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt

@ -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 {

Loading…
Cancel
Save