Horis 3 lat temu
rodzic
commit
8e535bbe1a

+ 18 - 14
app/src/main/java/io/legado/app/service/BaseReadAloudService.kt

@@ -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}"

+ 29 - 21
app/src/main/java/io/legado/app/service/TTSReadAloudService.kt

@@ -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
+            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
                 }
-                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")
-                        }
-                    }
+                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)
             }
+        }.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()
     }
 

+ 16 - 0
app/src/main/java/io/legado/app/ui/book/read/config/SpeakEngineDialog.kt

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