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