This commit is contained in:
Horis
2023-06-13 22:10:07 +08:00
parent 201fa7e05f
commit ed23874353
3 changed files with 65 additions and 37 deletions
@@ -29,6 +29,8 @@ import io.legado.app.ui.book.read.ReadBookActivity
import io.legado.app.ui.book.read.page.entities.TextChapter
import io.legado.app.utils.*
import kotlinx.coroutines.*
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
import splitties.init.appCtx
import splitties.systemservices.audioManager
import splitties.systemservices.powerManager
@@ -71,7 +73,7 @@ abstract class BaseReadAloudService : BaseService(),
private val mediaSessionCompat: MediaSessionCompat by lazy {
MediaSessionCompat(this, "readAloud")
}
internal val contentList = arrayListOf<String>()
internal var contentList = emptyList<String>()
internal var nowSpeak: Int = 0
internal var readAloudNumber: Int = 0
internal var textChapter: TextChapter? = null
@@ -132,6 +134,7 @@ abstract class BaseReadAloudService : BaseService(),
intent.getIntExtra("pageIndex", ReadBook.durPageIndex),
intent.getIntExtra("startPos", 0)
)
IntentAction.pause -> pauseReadAloud()
IntentAction.resume -> resumeReadAloud()
IntentAction.upTtsSpeechRate -> upSpeechRate(true)
@@ -144,20 +147,17 @@ abstract class BaseReadAloudService : BaseService(),
return super.onStartCommand(intent, flags, startId)
}
private fun newReadAloud(play: Boolean, pageIndex: Int, startPos: Int) {
this.pageIndex = pageIndex
private fun newReadAloud(play: Boolean, pageIndex: Int, startPos: Int) = launch(IO) {
this@BaseReadAloudService.pageIndex = pageIndex
textChapter = ReadBook.curTextChapter
textChapter?.let { textChapter ->
nowSpeak = 0
readAloudNumber = textChapter.getReadLength(pageIndex) + startPos
contentList.clear()
val readAloudByPage = getPrefBoolean(PreferKey.readAloudByPage)
textChapter.getNeedReadAloud(pageIndex, readAloudByPage, startPos)
.split("\n").forEach { text ->
if (text.isNotEmpty()) {
contentList.add(text)
}
}
val textChapter = textChapter ?: return@launch
nowSpeak = 0
readAloudNumber = textChapter.getReadLength(pageIndex) + startPos
val readAloudByPage = getPrefBoolean(PreferKey.readAloudByPage)
contentList = textChapter.getNeedReadAloud(pageIndex, readAloudByPage, startPos)
.split("\n")
.filter { it.isNotEmpty() }
launch(Main) {
if (play) play() else pageChanged = true
}
}
@@ -335,10 +335,12 @@ abstract class BaseReadAloudService : BaseService(),
AppLog.put("音频焦点获得")
}
}
AudioManager.AUDIOFOCUS_LOSS -> {
AppLog.put("音频焦点丢失,暂停朗读")
pauseReadAloud()
}
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
AppLog.put("音频焦点暂时丢失并会很快再次获得,暂停朗读")
if (!pause) {
@@ -346,6 +348,7 @@ abstract class BaseReadAloudService : BaseService(),
pauseReadAloud(false)
}
}
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
// 短暂丢失焦点,这种情况是被其他应用申请了短暂的焦点希望其他声音能压低音量(或者关闭声音)凸显这个声音(比如短信提示音),
AppLog.put("音频焦点短暂丢失,不做处理")
@@ -364,6 +367,7 @@ abstract class BaseReadAloudService : BaseService(),
R.string.read_aloud_timer,
timeMinute
)
else -> getString(R.string.read_aloud_t)
}
nTitle += ": ${ReadBook.book?.name}"
@@ -11,10 +11,12 @@ import io.legado.app.constant.EventBus
import io.legado.app.exception.NoStackTraceException
import io.legado.app.help.MediaHelp
import io.legado.app.help.config.AppConfig
import io.legado.app.help.coroutine.Coroutine
import io.legado.app.lib.dialogs.SelectItem
import io.legado.app.model.ReadAloud
import io.legado.app.model.ReadBook
import io.legado.app.utils.*
import kotlinx.coroutines.ensureActive
/**
* 本地朗读
@@ -24,6 +26,7 @@ class TTSReadAloudService : BaseReadAloudService(), TextToSpeech.OnInitListener
private var textToSpeech: TextToSpeech? = null
private var ttsInitFinish = false
private val ttsUtteranceListener = TTSUtteranceListener()
private var speakJob: Coroutine<*>? = null
override fun onCreate() {
super.onCreate()
@@ -74,30 +77,34 @@ class TTSReadAloudService : BaseReadAloudService(), TextToSpeech.OnInitListener
if (contentList.isEmpty()) {
AppLog.putDebug("朗读列表为空")
ReadBook.readAloud()
} else {
super.play()
kotlin.runCatching {
MediaHelp.playSilentSound(this@TTSReadAloudService)
val tts = textToSpeech ?: throw NoStackTraceException("tts is null")
var result = tts.speak("", TextToSpeech.QUEUE_FLUSH, null, null)
if (result == TextToSpeech.ERROR) {
clearTTS()
initTts()
return
}
for (i in nowSpeak until contentList.size) {
val text = contentList[i]
if (!text.matches(AppPattern.notReadAloudRegex)) {
result = tts.speak(text, TextToSpeech.QUEUE_ADD, null, AppConst.APP_TAG + i)
if (result == TextToSpeech.ERROR) {
AppLog.put("tts朗读出错:$text")
}
}
}
}.onFailure {
AppLog.put("tts朗读出错", it)
toastOnUi(it.localizedMessage)
return
}
super.play()
MediaHelp.playSilentSound(this@TTSReadAloudService)
speakJob?.cancel()
speakJob = execute {
val tts = textToSpeech ?: throw NoStackTraceException("tts is null")
var result = tts.speak("", TextToSpeech.QUEUE_FLUSH, null, null)
if (result == TextToSpeech.ERROR) {
clearTTS()
initTts()
return@execute
}
val contentList = contentList
for (i in nowSpeak until contentList.size) {
ensureActive()
val text = contentList[i]
if (text.matches(AppPattern.notReadAloudRegex)) {
continue
}
result = tts.speak(text, TextToSpeech.QUEUE_ADD, null, AppConst.APP_TAG + i)
if (result == TextToSpeech.ERROR) {
AppLog.put("tts朗读出错:$text")
}
}
}.onError {
AppLog.put("tts朗读出错", it)
toastOnUi(it.localizedMessage)
}
}
@@ -125,6 +132,7 @@ class TTSReadAloudService : BaseReadAloudService(), TextToSpeech.OnInitListener
*/
override fun pauseReadAloud(abandonFocus: Boolean) {
super.pauseReadAloud(abandonFocus)
speakJob?.cancel()
textToSpeech?.stop()
}
@@ -85,6 +85,21 @@ class SpeakEngineDialog(val callBack: CallBack) : BaseDialogFragment(R.layout.di
recyclerView.setEdgeEffectColor(primaryColor)
recyclerView.layoutManager = LinearLayoutManager(requireContext())
recyclerView.adapter = adapter
adapter.addHeaderView {
ItemHttpTtsBinding.inflate(layoutInflater, recyclerView, false).apply {
sysTtsViews.add(cbName)
ivEdit.gone()
ivMenuDelete.gone()
labelSys.visible()
cbName.text = "系统默认"
cbName.tag = ""
cbName.isChecked = GSON.fromJsonObject<SelectItem<String>>(ttsEngine)
.getOrNull()?.value.isNullOrEmpty()
cbName.setOnClickListener {
upTts(GSON.toJson(SelectItem("系统默认", "")))
}
}
}
viewModel.sysEngines.forEach { engine ->
adapter.addHeaderView {
ItemHttpTtsBinding.inflate(layoutInflater, recyclerView, false).apply {
@@ -147,6 +162,7 @@ class SpeakEngineDialog(val callBack: CallBack) : BaseDialogFragment(R.layout.di
mode = HandleFileContract.FILE
allowExtensions = arrayOf("txt", "json")
}
R.id.menu_import_onLine -> importAlert()
R.id.menu_export -> exportDirResult.launch {
mode = HandleFileContract.EXPORT