Jelajahi Sumber

refactor(read): 删除不可达的 TTS 引擎配置 UI(净删 838 行)

阅读页里这套 TTS 引擎配置从 UI 到状态整条不可达,用户确认删除。

不可达的证据链:
- SpeakEngineConfigSheet 与 HttpTtsEditSheet(ReadAloudConfigContent.kt)
  全仓无调用点;
- ReadBookSheet.SpeakEngineConfig / HttpTtsEdit 被 VM 设置,但没有任何
  Screen 有匹配分支——所以原本点进去会把当前弹层关掉、什么都不显示;
- 19 个相关 intent 的派发点全在那两个死 composable 内部,
  ReadBookRouteScreen 的两个 picker launcher 也只由死路径触发的 effect 唤起;
- 5 个状态字段(ttsEngineItems / selectedTtsEngine / speakEngineName /
  editingHttpTts / httpTtsImportState)只被那两个死 composable 读。

活的 TTS 管理界面是 ui/book/readaloud/cloudtts/,不受影响(CloudTtsEffect
是另一套 sealed 层级)。

**保留了唯一的活副作用**:syncConfiguredTtsVoices(写声音目录)。它在 VM
init 已有一次独立调用;原来 loadTtsEngineItems 里那次是"开朗读配置弹层时
刷新一遍",用于接住在 cloudtts 页新增的引擎,所以 ShowReadAloudConfig
分支里改成直接 execute { syncConfiguredTtsVoices() },行为不变。

删除面:contract 38 行(5 字段 + 19 intent + 3 effect + 2 sheet +
ReadBookTtsEngineItem)、VM 396 行、ReadAloudConfigContent 374 行、
RouteScreen 33 行、Controller 3 行;另清 46 个因此变无用的 import
(只删本次改动导致无用的,既存无用 import 未动)。
ReadBookViewModel 4146 → 3756 行。

验证:testAppDebugUnitTest(426 passed) + lintAppDebug +
verifyConfigArchitecture + assembleAppDebug 全绿。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
5151561 3 bulan lalu
induk
melakukan
93f7efa751

+ 0 - 38
app/src/main/java/io/legado/app/ui/book/read/ReadBookContract.kt

@@ -11,7 +11,6 @@ import io.legado.app.data.entities.BookProgress
 import io.legado.app.data.entities.BookSource
 import io.legado.app.data.entities.BookSource
 import io.legado.app.data.entities.Bookmark
 import io.legado.app.data.entities.Bookmark
 import io.legado.app.data.entities.HighlightRule
 import io.legado.app.data.entities.HighlightRule
-import io.legado.app.data.entities.HttpTTS
 import io.legado.app.data.entities.ReplaceRule
 import io.legado.app.data.entities.ReplaceRule
 import io.legado.app.data.repository.ReadAloudSettingsRepository
 import io.legado.app.data.repository.ReadAloudSettingsRepository
 import io.legado.app.domain.model.readaloud.SpeechRoleType
 import io.legado.app.domain.model.readaloud.SpeechRoleType
@@ -22,7 +21,6 @@ import io.legado.app.ui.book.read.page.entities.TextPage
 import io.legado.app.ui.book.read.page.entities.TextPos
 import io.legado.app.ui.book.read.page.entities.TextPos
 import io.legado.app.ui.book.read.sheet.ReaderBookSheetTab
 import io.legado.app.ui.book.read.sheet.ReaderBookSheetTab
 import io.legado.app.ui.book.searchContent.SearchResult
 import io.legado.app.ui.book.searchContent.SearchResult
-import io.legado.app.ui.widget.components.importComponents.BaseImportUiState
 import kotlinx.collections.immutable.ImmutableList
 import kotlinx.collections.immutable.ImmutableList
 import kotlinx.collections.immutable.ImmutableMap
 import kotlinx.collections.immutable.ImmutableMap
 import kotlinx.collections.immutable.persistentListOf
 import kotlinx.collections.immutable.persistentListOf
@@ -253,11 +251,6 @@ data class ReadBookUiState(
     val isReadingProgressSyncConfigured: Boolean = false,
     val isReadingProgressSyncConfigured: Boolean = false,
     // Content edit
     // Content edit
     // 正文编辑域状态见 ContentEditUiState —— 由 ReadContentEditDelegate 独立持有
     // 正文编辑域状态见 ContentEditUiState —— 由 ReadContentEditDelegate 独立持有
-    val ttsEngineItems: ImmutableList<ReadBookTtsEngineItem> = persistentListOf(),
-    val selectedTtsEngine: String? = null,
-    val speakEngineName: String = "",
-    val editingHttpTts: HttpTTS? = null,
-    val httpTtsImportState: BaseImportUiState<HttpTTS> = BaseImportUiState.Idle,
     val preDownloadNum: Int = 10,
     val preDownloadNum: Int = 10,
     val preSynthesisConcurrency: Int = 3,
     val preSynthesisConcurrency: Int = 3,
     val audioCacheCleanTime: Int = 10,
     val audioCacheCleanTime: Int = 10,
@@ -370,13 +363,6 @@ data class ReadMenuConfig(
     val titleBarCompact: Boolean = false,
     val titleBarCompact: Boolean = false,
 )
 )
 
 
-@Immutable
-data class ReadBookTtsEngineItem(
-    val title: String,
-    val value: String?,
-    val loginUrl: String? = null,
-)
-
 @Immutable
 @Immutable
 data class ReadBookButtonConfigItem(
 data class ReadBookButtonConfigItem(
     val id: String,
     val id: String,
@@ -687,33 +673,14 @@ sealed interface ReadBookIntent {
 
 
     // Read aloud config (needs Activity for DialogFragment)
     // Read aloud config (needs Activity for DialogFragment)
     data object ShowReadAloudConfig : ReadBookIntent
     data object ShowReadAloudConfig : ReadBookIntent
-    data object SelectSpeakEngine : ReadBookIntent
     data object OpenPreDownloadNumPicker : ReadBookIntent
     data object OpenPreDownloadNumPicker : ReadBookIntent
     data object OpenPreSynthesisConcurrencyPicker : ReadBookIntent
     data object OpenPreSynthesisConcurrencyPicker : ReadBookIntent
     data object OpenParagraphIntervalPicker : ReadBookIntent
     data object OpenParagraphIntervalPicker : ReadBookIntent
     data object OpenCacheCleanTimePicker : ReadBookIntent
     data object OpenCacheCleanTimePicker : ReadBookIntent
-    data class ApplySpeakEngine(val value: String?) : ReadBookIntent
     data class ApplyPreDownloadNum(val value: Int) : ReadBookIntent
     data class ApplyPreDownloadNum(val value: Int) : ReadBookIntent
     data class ApplyPreSynthesisConcurrency(val value: Int) : ReadBookIntent
     data class ApplyPreSynthesisConcurrency(val value: Int) : ReadBookIntent
     data class ApplyAudioCacheCleanTime(val value: Int) : ReadBookIntent
     data class ApplyAudioCacheCleanTime(val value: Int) : ReadBookIntent
     data class ApplyParagraphInterval(val value: Int) : ReadBookIntent
     data class ApplyParagraphInterval(val value: Int) : ReadBookIntent
-    data class EditHttpTts(val engineId: Long? = null) : ReadBookIntent
-    data class DeleteHttpTts(val engineId: Long) : ReadBookIntent
-    data class SaveHttpTts(val httpTTS: HttpTTS) : ReadBookIntent
-    data class ApplySpeakEnginePerBook(val value: String?) : ReadBookIntent
-    data class OpenHttpTtsLogin(val engineId: Long) : ReadBookIntent
-    data class ImportHttpTtsJson(val json: String) : ReadBookIntent
-    data class ImportHttpTtsSource(val text: String) : ReadBookIntent
-    data object ImportHttpTtsFile : ReadBookIntent
-    data class ImportHttpTtsFileSelected(val uri: Uri) : ReadBookIntent
-    data object CancelHttpTtsImport : ReadBookIntent
-    data class ToggleHttpTtsImportSelection(val index: Int) : ReadBookIntent
-    data class ToggleHttpTtsImportAll(val isSelected: Boolean) : ReadBookIntent
-    data class UpdateHttpTtsImportItem(val index: Int, val httpTTS: HttpTTS) : ReadBookIntent
-    data object SaveImportedHttpTts : ReadBookIntent
-    data object ExportAllHttpTts : ReadBookIntent
-    data object ExportAllHttpTtsAsUrl : ReadBookIntent
-    data class ExportHttpTtsToFile(val uri: Uri) : ReadBookIntent
     data class SetReadAloudIgnoreAudioFocus(val value: Boolean) : ReadBookIntent
     data class SetReadAloudIgnoreAudioFocus(val value: Boolean) : ReadBookIntent
     data class SetReadAloudPauseOnPhoneCall(val value: Boolean) : ReadBookIntent
     data class SetReadAloudPauseOnPhoneCall(val value: Boolean) : ReadBookIntent
     data class SetReadAloudWakeLock(val value: Boolean) : ReadBookIntent
     data class SetReadAloudWakeLock(val value: Boolean) : ReadBookIntent
@@ -885,9 +852,6 @@ sealed interface ReadBookEffect {
     data class OpenMenuCustomIconPicker(val id: String) : ReadBookEffect
     data class OpenMenuCustomIconPicker(val id: String) : ReadBookEffect
     data class OpenTitleBarCustomIconPicker(val id: String) : ReadBookEffect
     data class OpenTitleBarCustomIconPicker(val id: String) : ReadBookEffect
     data object OpenSystemTtsSettings : ReadBookEffect
     data object OpenSystemTtsSettings : ReadBookEffect
-    data object OpenHttpTtsImportPicker : ReadBookEffect
-    data object OpenHttpTtsExportPicker : ReadBookEffect
-    data class OpenHttpTtsLogin(val engineId: Long) : ReadBookEffect
     data object OpenTtsEnginesAndVoices : ReadBookEffect
     data object OpenTtsEnginesAndVoices : ReadBookEffect
     data object OpenTtsCache : ReadBookEffect
     data object OpenTtsCache : ReadBookEffect
     data class OpenBookVoiceCasting(val bookUrl: String) : ReadBookEffect
     data class OpenBookVoiceCasting(val bookUrl: String) : ReadBookEffect
@@ -945,8 +909,6 @@ sealed interface ReadBookSheet {
     data object BgTextConfig : ReadBookSheet
     data object BgTextConfig : ReadBookSheet
     data object ReadAloudConfig : ReadBookSheet
     data object ReadAloudConfig : ReadBookSheet
     data object ReadAloudPlayer : ReadBookSheet
     data object ReadAloudPlayer : ReadBookSheet
-    data object SpeakEngineConfig : ReadBookSheet
-    data class HttpTtsEdit(val engineId: Long? = null) : ReadBookSheet
     data object PreDownloadConfig : ReadBookSheet
     data object PreDownloadConfig : ReadBookSheet
     data object PreSynthesisConcurrencyConfig : ReadBookSheet
     data object PreSynthesisConcurrencyConfig : ReadBookSheet
     data object AudioCacheCleanConfig : ReadBookSheet
     data object AudioCacheCleanConfig : ReadBookSheet

+ 0 - 3
app/src/main/java/io/legado/app/ui/book/read/ReadBookController.kt

@@ -1141,9 +1141,6 @@ class ReadBookController(
             is ReadBookEffect.OpenMenuCustomIconPicker,
             is ReadBookEffect.OpenMenuCustomIconPicker,
             is ReadBookEffect.OpenTitleBarCustomIconPicker,
             is ReadBookEffect.OpenTitleBarCustomIconPicker,
             is ReadBookEffect.OpenSystemTtsSettings,
             is ReadBookEffect.OpenSystemTtsSettings,
-            is ReadBookEffect.OpenHttpTtsImportPicker,
-            is ReadBookEffect.OpenHttpTtsExportPicker,
-            is ReadBookEffect.OpenHttpTtsLogin,
             ReadBookEffect.OpenTtsEnginesAndVoices,
             ReadBookEffect.OpenTtsEnginesAndVoices,
             ReadBookEffect.OpenTtsCache,
             ReadBookEffect.OpenTtsCache,
             is ReadBookEffect.OpenBookVoiceCasting,
             is ReadBookEffect.OpenBookVoiceCasting,

+ 0 - 33
app/src/main/java/io/legado/app/ui/book/read/ReadBookRouteScreen.kt

@@ -278,18 +278,6 @@ fun ReadBookRouteScreen(
         }
         }
     }
     }
 
 
-    val importHttpTtsPicker = rememberLauncherForActivityResult(
-        ActivityResultContracts.OpenDocument()
-    ) { uri ->
-        uri?.let { viewModel.onIntent(ReadBookIntent.ImportHttpTtsFileSelected(it)) }
-    }
-
-    val exportHttpTtsPicker = rememberLauncherForActivityResult(
-        ActivityResultContracts.CreateDocument("application/json")
-    ) { uri ->
-        uri?.let { viewModel.onIntent(ReadBookIntent.ExportHttpTtsToFile(it)) }
-    }
-
     val importHighlightRulePicker = rememberLauncherForActivityResult(
     val importHighlightRulePicker = rememberLauncherForActivityResult(
         ActivityResultContracts.OpenDocument()
         ActivityResultContracts.OpenDocument()
     ) { uri ->
     ) { uri ->
@@ -347,14 +335,6 @@ fun ReadBookRouteScreen(
                                     }
                                     }
                                 )
                                 )
                             }
                             }
-                            is ReadBookEffect.OpenHttpTtsLogin -> {
-                                context.startActivity(
-                                    Intent(context, SourceLoginActivity::class.java).apply {
-                                        putExtra("type", "httpTts")
-                                        putExtra("key", effect.engineId.toString())
-                                    }
-                                )
-                            }
                             is ReadBookEffect.OpenWebView -> {
                             is ReadBookEffect.OpenWebView -> {
                                 context.startActivity(
                                 context.startActivity(
                                     Intent(context, WebViewActivity::class.java).apply {
                                     Intent(context, WebViewActivity::class.java).apply {
@@ -455,19 +435,6 @@ fun ReadBookRouteScreen(
                             is ReadBookEffect.TtsCacheCleared -> {
                             is ReadBookEffect.TtsCacheCleared -> {
                                 context.toastOnUi(effect.message)
                                 context.toastOnUi(effect.message)
                             }
                             }
-                            is ReadBookEffect.OpenHttpTtsImportPicker -> {
-                                importHttpTtsPicker.launch(
-                                    arrayOf(
-                                        "application/json",
-                                        "text/plain"
-                                    )
-                                )
-                            }
-
-                            is ReadBookEffect.OpenHttpTtsExportPicker -> {
-                                exportHttpTtsPicker.launch("httpTTS.json")
-                            }
-
                             is ReadBookEffect.OpenHighlightRuleImportPicker -> {
                             is ReadBookEffect.OpenHighlightRuleImportPicker -> {
                                 importHighlightRulePicker.launch(
                                 importHighlightRulePicker.launch(
                                     arrayOf(
                                     arrayOf(

+ 3 - 393
app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt

@@ -6,15 +6,12 @@ import android.content.Intent
 import android.net.Uri
 import android.net.Uri
 import android.provider.OpenableColumns
 import android.provider.OpenableColumns
 import android.speech.tts.TextToSpeech
 import android.speech.tts.TextToSpeech
-import android.util.Base64
 import androidx.core.graphics.toColorInt
 import androidx.core.graphics.toColorInt
 import androidx.lifecycle.viewModelScope
 import androidx.lifecycle.viewModelScope
 import io.legado.app.BuildConfig
 import io.legado.app.BuildConfig
 import io.legado.app.R
 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.AppLog
 import io.legado.app.constant.AppLog
-import io.legado.app.constant.AppPattern
 import io.legado.app.constant.BookType
 import io.legado.app.constant.BookType
 import io.legado.app.constant.EventBus
 import io.legado.app.constant.EventBus
 import io.legado.app.constant.PreferKey
 import io.legado.app.constant.PreferKey
@@ -85,12 +82,8 @@ import io.legado.app.help.book.removeType
 import io.legado.app.help.book.simulatedTotalChapterNum
 import io.legado.app.help.book.simulatedTotalChapterNum
 import io.legado.app.help.config.ReadBookConfig
 import io.legado.app.help.config.ReadBookConfig
 import io.legado.app.help.coroutine.Coroutine
 import io.legado.app.help.coroutine.Coroutine
-import io.legado.app.help.http.decompressed
-import io.legado.app.help.http.newCallResponseBody
-import io.legado.app.help.http.okHttpClient
 import io.legado.app.help.http.text
 import io.legado.app.help.http.text
 import io.legado.app.help.source.getSourceType
 import io.legado.app.help.source.getSourceType
-import io.legado.app.lib.dialogs.SelectItem
 import io.legado.app.model.ImageProvider
 import io.legado.app.model.ImageProvider
 import io.legado.app.model.ReadAloud
 import io.legado.app.model.ReadAloud
 import io.legado.app.model.ReadAloudSessionStore
 import io.legado.app.model.ReadAloudSessionStore
@@ -110,25 +103,16 @@ import io.legado.app.model.webBook.WebBook
 import io.legado.app.service.BaseReadAloudService
 import io.legado.app.service.BaseReadAloudService
 import io.legado.app.ui.book.read.page.entities.TextChapter
 import io.legado.app.ui.book.read.page.entities.TextChapter
 import io.legado.app.ui.book.searchContent.SearchResult
 import io.legado.app.ui.book.searchContent.SearchResult
-import io.legado.app.ui.widget.components.importComponents.BaseImportUiState
-import io.legado.app.ui.widget.components.importComponents.ImportItemWrapper
-import io.legado.app.ui.widget.components.importComponents.ImportStatus
 import io.legado.app.utils.GSON
 import io.legado.app.utils.GSON
 import io.legado.app.utils.ImageSaveUtils
 import io.legado.app.utils.ImageSaveUtils
 import io.legado.app.utils.NetworkUtils
 import io.legado.app.utils.NetworkUtils
-import io.legado.app.utils.StringUtils
-import io.legado.app.utils.fromJsonArray
 import io.legado.app.utils.fromJsonObject
 import io.legado.app.utils.fromJsonObject
 import io.legado.app.utils.hexString
 import io.legado.app.utils.hexString
 import io.legado.app.utils.isAbsUrl
 import io.legado.app.utils.isAbsUrl
-import io.legado.app.utils.isDataUrl
-import io.legado.app.utils.isJsonArray
-import io.legado.app.utils.isJsonObject
 import io.legado.app.utils.isTrue
 import io.legado.app.utils.isTrue
 import io.legado.app.utils.mapParallelSafe
 import io.legado.app.utils.mapParallelSafe
 import io.legado.app.utils.openUrl
 import io.legado.app.utils.openUrl
 import io.legado.app.utils.postEvent
 import io.legado.app.utils.postEvent
-import io.legado.app.utils.sendToClip
 import io.legado.app.utils.toStringArray
 import io.legado.app.utils.toStringArray
 import kotlinx.collections.immutable.toImmutableList
 import kotlinx.collections.immutable.toImmutableList
 import kotlinx.collections.immutable.toImmutableMap
 import kotlinx.collections.immutable.toImmutableMap
@@ -1060,17 +1044,9 @@ class ReadBookViewModel(
             is ReadBookIntent.ExitWithoutAddingCurrentBookToBookshelf -> removeCurrentNotShelfBookAndFinish()
             is ReadBookIntent.ExitWithoutAddingCurrentBookToBookshelf -> removeCurrentNotShelfBookAndFinish()
 
 
             is ReadBookIntent.ShowReadAloudConfig -> {
             is ReadBookIntent.ShowReadAloudConfig -> {
-                _uiState.update {
-                    it.copy(
-                        speakEngineName = computeSpeakEngineName(),
-                        activeSheet = ReadBookSheet.ReadAloudConfig,
-                    )
-                }
-                loadTtsEngineItems()
-            }
-
-            is ReadBookIntent.SelectSpeakEngine -> {
-                showSpeakEngineConfig()
+                _uiState.update { it.copy(activeSheet = ReadBookSheet.ReadAloudConfig) }
+                // 朗读引擎可能在 cloudtts 页被改过,开弹层时刷一次声音目录
+                execute { syncConfiguredTtsVoices() }
             }
             }
 
 
             is ReadBookIntent.OpenPreDownloadNumPicker -> {
             is ReadBookIntent.OpenPreDownloadNumPicker -> {
@@ -1112,21 +1088,6 @@ class ReadBookViewModel(
                 }
                 }
             }
             }
 
 
-            is ReadBookIntent.ApplySpeakEngine -> {
-                ReadBook.book?.setTtsEngine(null)
-                viewModelScope.launch(start = CoroutineStart.UNDISPATCHED) {
-                    readAloudSettingsRepository.update { it.copy(ttsEngine = intent.value) }
-                    ReadAloud.upReadAloudClass()
-                    _uiState.update {
-                        it.copy(
-                            selectedTtsEngine = ReadAloud.ttsEngine,
-                            speakEngineName = computeSpeakEngineName(),
-                            activeSheet = ReadBookSheet.ReadAloudConfig,
-                        )
-                    }
-                }
-            }
-
             is ReadBookIntent.ApplyPreDownloadNum -> {
             is ReadBookIntent.ApplyPreDownloadNum -> {
                 viewModelScope.launch(start = CoroutineStart.UNDISPATCHED) {
                 viewModelScope.launch(start = CoroutineStart.UNDISPATCHED) {
                     readSettingsRepository.setPreDownloadNum(intent.value)
                     readSettingsRepository.setPreDownloadNum(intent.value)
@@ -1180,141 +1141,6 @@ class ReadBookViewModel(
                 }
                 }
             }
             }
 
 
-            is ReadBookIntent.EditHttpTts -> {
-                if (intent.engineId == null) {
-                    _uiState.update {
-                        it.copy(
-                            editingHttpTts = HttpTTS(),
-                            activeSheet = ReadBookSheet.HttpTtsEdit(),
-                        )
-                    }
-                } else {
-                    execute {
-                        httpTtsRepository.findById(intent.engineId)
-                    }.onSuccess { tts ->
-                        _uiState.update {
-                            it.copy(
-                                editingHttpTts = tts,
-                                activeSheet = ReadBookSheet.HttpTtsEdit(intent.engineId),
-                            )
-                        }
-                    }
-                }
-            }
-
-            is ReadBookIntent.DeleteHttpTts -> {
-                execute {
-                    httpTtsRepository.findById(intent.engineId)?.let { tts ->
-                        httpTtsRepository.delete(tts)
-                    }
-                }.onSuccess {
-                    loadTtsEngineItems()
-                    if (ReadAloud.ttsEngine == intent.engineId.toString()) {
-                        viewModelScope.launch(start = CoroutineStart.UNDISPATCHED) {
-                            readAloudSettingsRepository.update { it.copy(ttsEngine = null) }
-                            ReadAloud.upReadAloudClass()
-                        }
-                    }
-                }
-            }
-
-            is ReadBookIntent.SaveHttpTts -> {
-                execute {
-                    httpTtsRepository.insert(intent.httpTTS)
-                }.onSuccess {
-                    loadTtsEngineItems()
-                    _uiState.update {
-                        it.copy(
-                            editingHttpTts = null,
-                            activeSheet = ReadBookSheet.SpeakEngineConfig,
-                        )
-                    }
-                }
-            }
-
-            is ReadBookIntent.ApplySpeakEnginePerBook -> {
-                ReadBook.book?.setTtsEngine(intent.value)
-                ReadAloud.upReadAloudClass()
-                _uiState.update {
-                    it.copy(
-                        selectedTtsEngine = ReadAloud.ttsEngine,
-                        speakEngineName = computeSpeakEngineName(),
-                        activeSheet = ReadBookSheet.ReadAloudConfig,
-                    )
-                }
-            }
-
-            is ReadBookIntent.OpenHttpTtsLogin -> {
-                _effects.tryEmit(ReadBookEffect.OpenHttpTtsLogin(intent.engineId))
-            }
-
-            is ReadBookIntent.ImportHttpTtsJson -> {
-                importHttpTtsSource(intent.json)
-            }
-
-            is ReadBookIntent.ImportHttpTtsSource -> {
-                importHttpTtsSource(intent.text)
-            }
-
-            is ReadBookIntent.ExportAllHttpTts -> {
-                _effects.tryEmit(ReadBookEffect.OpenHttpTtsExportPicker)
-            }
-
-            is ReadBookIntent.ExportAllHttpTtsAsUrl -> {
-                execute {
-                    val json = exportHttpTtsJson()
-                    uploadRepository.upload(
-                        fileName = "httpTTS.json",
-                        file = json,
-                        contentType = "application/json"
-                    )
-                }.onSuccess { url ->
-                    context.sendToClip(url)
-                    _effects.tryEmit(ReadBookEffect.ShowToast(context.getString(R.string.copy_url)))
-                }
-            }
-
-            is ReadBookIntent.ExportHttpTtsToFile -> {
-                execute {
-                    val json = exportHttpTtsJson()
-                    context.contentResolver.openOutputStream(intent.uri)?.use { os ->
-                        os.write(json.toByteArray())
-                    }
-                }.onSuccess {
-                    _effects.tryEmit(ReadBookEffect.ShowToast(context.getString(R.string.export_success)))
-                }
-            }
-
-            is ReadBookIntent.ImportHttpTtsFile -> {
-                _effects.tryEmit(ReadBookEffect.OpenHttpTtsImportPicker)
-            }
-
-            is ReadBookIntent.ImportHttpTtsFileSelected -> {
-                execute<String?> {
-                    val text = context.contentResolver.openInputStream(intent.uri)
-                        ?.use { it.reader().readText() }
-                    text
-                }.onSuccess { text ->
-                    if (!text.isNullOrBlank()) importHttpTtsSource(text)
-                }
-            }
-
-            ReadBookIntent.CancelHttpTtsImport -> cancelHttpTtsImport()
-
-            is ReadBookIntent.ToggleHttpTtsImportSelection -> {
-                toggleHttpTtsImportSelection(intent.index)
-            }
-
-            is ReadBookIntent.ToggleHttpTtsImportAll -> {
-                toggleHttpTtsImportAll(intent.isSelected)
-            }
-
-            is ReadBookIntent.UpdateHttpTtsImportItem -> {
-                updateHttpTtsImportItem(intent.index, intent.httpTTS)
-            }
-
-            ReadBookIntent.SaveImportedHttpTts -> saveImportedHttpTts()
-
             is ReadBookIntent.SetReadAloudIgnoreAudioFocus -> {
             is ReadBookIntent.SetReadAloudIgnoreAudioFocus -> {
                 viewModelScope.launch {
                 viewModelScope.launch {
                     readAloudSettingsRepository.update { it.copy(ignoreAudioFocus = intent.value) }
                     readAloudSettingsRepository.update { it.copy(ignoreAudioFocus = intent.value) }
@@ -1781,48 +1607,6 @@ class ReadBookViewModel(
         ReadBook.cancelPreDownloadTask()
         ReadBook.cancelPreDownloadTask()
     }
     }
 
 
-    private fun showSpeakEngineConfig() {
-        loadTtsEngineItems {
-            _uiState.update { it.copy(activeSheet = ReadBookSheet.SpeakEngineConfig) }
-        }
-    }
-
-    private fun loadTtsEngineItems(onSuccess: (() -> Unit)? = null) {
-        execute {
-            val systemTtsLabel = context.getString(R.string.system_tts)
-            val httpTtsList = httpTtsRepository.getAll()
-            syncConfiguredTtsVoices(systemTtsLabel, httpTtsList)
-            buildList {
-                add(ReadBookTtsEngineItem(systemTtsLabel, null))
-                sysEngines.forEach { engine ->
-                    add(
-                        ReadBookTtsEngineItem(
-                            title = engine.label,
-                            value = GSON.toJson(SelectItem(engine.label, engine.name)),
-                        )
-                    )
-                }
-                httpTtsList.forEach { httpTts ->
-                    add(
-                        ReadBookTtsEngineItem(
-                            title = httpTts.name,
-                            value = httpTts.id.toString(),
-                            loginUrl = httpTts.loginUrl,
-                        )
-                    )
-                }
-            }
-        }.onSuccess { items ->
-            _uiState.update {
-                it.copy(
-                    ttsEngineItems = items.toImmutableList(),
-                    selectedTtsEngine = ReadAloud.ttsEngine,
-                )
-            }
-            onSuccess?.invoke()
-        }
-    }
-
     private suspend fun syncConfiguredTtsVoices(
     private suspend fun syncConfiguredTtsVoices(
         systemTtsLabel: String = context.getString(R.string.system_tts),
         systemTtsLabel: String = context.getString(R.string.system_tts),
         httpTtsList: List<HttpTTS> = httpTtsRepository.getAllSync(),
         httpTtsList: List<HttpTTS> = httpTtsRepository.getAllSync(),
@@ -1861,179 +1645,6 @@ class ReadBookViewModel(
         )
         )
     }
     }
 
 
-    private fun importHttpTtsSource(text: String) {
-        _uiState.update {
-            it.copy(httpTtsImportState = BaseImportUiState.Loading)
-        }
-        execute {
-            val list = importHttpTtsSourceAwait(text.trim())
-            val items = list.map { httpTTS ->
-                val old = httpTtsRepository.findById(httpTTS.id)
-                val status = when {
-                    old == null -> ImportStatus.New
-                    httpTTS.lastUpdateTime > old.lastUpdateTime -> ImportStatus.Update
-                    else -> ImportStatus.Existing
-                }
-                ImportItemWrapper(
-                    data = httpTTS,
-                    oldData = old,
-                    status = status,
-                    isSelected = status != ImportStatus.Existing,
-                )
-            }
-            if (items.isEmpty()) {
-                throw NoStackTraceException(context.getString(R.string.wrong_format))
-            }
-            BaseImportUiState.Success(
-                source = text,
-                items = items,
-            )
-        }.onSuccess { importState ->
-            _uiState.update {
-                it.copy(httpTtsImportState = importState)
-            }
-        }.onError {
-            AppLog.put("导入朗读引擎失败\n${it.localizedMessage}", it, true)
-            _uiState.update { state ->
-                state.copy(
-                    httpTtsImportState = BaseImportUiState.Error(
-                        it.localizedMessage ?: context.getString(R.string.wrong_format)
-                    )
-                )
-            }
-        }
-    }
-
-    private suspend fun importHttpTtsSourceAwait(text: String): List<HttpTTS> {
-        return when {
-            text.isHttpTtsImportUri() -> {
-                val src = Uri.parse(text).getQueryParameter("src")
-                    ?: throw NoStackTraceException(context.getString(R.string.wrong_format))
-                importHttpTtsSourceAwait(src)
-            }
-            text.isJsonObject() -> listOf(HttpTTS.fromJson(text).getOrThrow())
-            text.isJsonArray() -> HttpTTS.fromJsonArray(text).getOrThrow()
-            text.isDataUrl() -> {
-                val data = AppPattern.dataUriRegex.find(text)?.groupValues?.getOrNull(1)
-                    ?: throw NoStackTraceException(context.getString(R.string.wrong_format))
-                val body = Base64.decode(data, Base64.DEFAULT).toString(Charsets.UTF_8)
-                importHttpTtsSourceAwait(body)
-            }
-            text.isAbsUrl() -> {
-                val body = okHttpClient.newCallResponseBody {
-                    if (text.endsWith("#requestWithoutUA")) {
-                        url(text.substringBeforeLast("#requestWithoutUA"))
-                        header(AppConst.UA_NAME, "null")
-                    } else {
-                        url(text)
-                    }
-                }.decompressed().text()
-                importHttpTtsSourceAwait(body)
-            }
-            else -> throw NoStackTraceException(context.getString(R.string.wrong_format))
-        }
-    }
-
-    private fun cancelHttpTtsImport() {
-        _uiState.update {
-            it.copy(
-                httpTtsImportState = BaseImportUiState.Idle,
-                activeSheet = ReadBookSheet.SpeakEngineConfig,
-            )
-        }
-    }
-
-    private fun toggleHttpTtsImportSelection(index: Int) {
-        val importState = _uiState.value.httpTtsImportState
-            as? BaseImportUiState.Success<HttpTTS> ?: return
-        if (index !in importState.items.indices) return
-        val items = importState.items.toMutableList()
-        val item = items[index]
-        items[index] = item.copy(isSelected = !item.isSelected)
-        _uiState.update {
-            it.copy(httpTtsImportState = importState.copy(items = items))
-        }
-    }
-
-    private fun toggleHttpTtsImportAll(isSelected: Boolean) {
-        val importState = _uiState.value.httpTtsImportState
-            as? BaseImportUiState.Success<HttpTTS> ?: return
-        _uiState.update {
-            it.copy(
-                httpTtsImportState = importState.copy(
-                    items = importState.items.map { item ->
-                        item.copy(isSelected = isSelected)
-                    }
-                )
-            )
-        }
-    }
-
-    private fun updateHttpTtsImportItem(index: Int, httpTTS: HttpTTS) {
-        val importState = _uiState.value.httpTtsImportState
-            as? BaseImportUiState.Success<HttpTTS> ?: return
-        if (index !in importState.items.indices) return
-        val items = importState.items.toMutableList()
-        items[index] = items[index].copy(data = httpTTS)
-        _uiState.update {
-            it.copy(
-                httpTtsImportState = importState.copy(
-                    items = items,
-                    version = importState.version + 1,
-                )
-            )
-        }
-    }
-
-    private fun saveImportedHttpTts() {
-        val importState = _uiState.value.httpTtsImportState
-            as? BaseImportUiState.Success<HttpTTS> ?: return
-        val selected = importState.items
-            .filter { it.isSelected }
-            .map { it.data }
-        if (selected.isEmpty()) return
-        execute {
-            httpTtsRepository.insert(*selected.toTypedArray())
-        }.onSuccess {
-            loadTtsEngineItems()
-            _uiState.update {
-                it.copy(
-                    httpTtsImportState = BaseImportUiState.Idle,
-                    activeSheet = ReadBookSheet.SpeakEngineConfig,
-                )
-            }
-            _effects.tryEmit(ReadBookEffect.ShowToast(context.getString(R.string.success)))
-        }.onError {
-            AppLog.put("保存朗读引擎失败\n${it.localizedMessage}", it, true)
-            _uiState.update { state ->
-                state.copy(
-                    httpTtsImportState = BaseImportUiState.Error(
-                        it.localizedMessage ?: context.getString(R.string.wrong_format)
-                    )
-                )
-            }
-        }
-    }
-
-    private fun exportHttpTtsJson(): String {
-        return GSON.toJson(httpTtsRepository.getAllSync())
-    }
-
-    private fun computeSpeakEngineName(): String {
-        val ttsEngine = ReadAloud.ttsEngine
-            ?: return context.getString(R.string.system_tts)
-        if (StringUtils.isNumeric(ttsEngine)) {
-            return httpTtsRepository.getNameSync(ttsEngine.toLong())
-                ?: context.getString(R.string.system_tts)
-        }
-        return GSON.fromJsonObject<SelectItem<String>>(ttsEngine)
-            .getOrNull()?.title
-            ?: context.getString(R.string.system_tts)
-    }
-
-    /**
-     * Called from the network changed listener (registered by route).
-     */
     fun onNetworkChanged() {
     fun onNetworkChanged() {
         if (
         if (
             backupSettingsGateway.currentSettings.syncBookProgressPlus &&
             backupSettingsGateway.currentSettings.syncBookProgressPlus &&
@@ -2348,7 +1959,6 @@ class ReadBookViewModel(
                         speechAnalysisMode = prefs.speechAnalysisMode,
                         speechAnalysisMode = prefs.speechAnalysisMode,
                         useMultiSpeaker = prefs.useMultiSpeaker,
                         useMultiSpeaker = prefs.useMultiSpeaker,
                         defaultReadAloudInterface = prefs.defaultInterface,
                         defaultReadAloudInterface = prefs.defaultInterface,
-                        selectedTtsEngine = prefs.ttsEngine,
                         preDownloadNum = _readPreferences.value.preDownloadNum,
                         preDownloadNum = _readPreferences.value.preDownloadNum,
                         audioCacheCleanTime = prefs.audioCacheCleanTime,
                         audioCacheCleanTime = prefs.audioCacheCleanTime,
                         readAloudParagraphInterval = prefs.ttsParagraphInterval,
                         readAloudParagraphInterval = prefs.ttsParagraphInterval,

+ 0 - 374
app/src/main/java/io/legado/app/ui/book/read/sheet/ReadAloudConfigContent.kt

@@ -1,60 +1,32 @@
 package io.legado.app.ui.book.read.sheet
 package io.legado.app.ui.book.read.sheet
 
 
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.foundation.lazy.items
 import androidx.compose.foundation.pager.HorizontalPager
 import androidx.compose.foundation.pager.HorizontalPager
 import androidx.compose.foundation.pager.rememberPagerState
 import androidx.compose.foundation.pager.rememberPagerState
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.verticalScroll
 import androidx.compose.foundation.verticalScroll
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Add
-import androidx.compose.material.icons.filled.Delete
-import androidx.compose.material.icons.filled.Edit
-import androidx.compose.material.icons.filled.MoreVert
-import androidx.compose.material.icons.filled.Save
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.runtime.setValue
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.LocalClipboardManager
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.dp
 import io.legado.app.R
 import io.legado.app.R
 import io.legado.app.constant.ReadAloudBgMode
 import io.legado.app.constant.ReadAloudBgMode
-import io.legado.app.data.entities.HttpTTS
 import io.legado.app.ui.book.read.ReadBookIntent
 import io.legado.app.ui.book.read.ReadBookIntent
 import io.legado.app.ui.book.read.ReadBookUiState
 import io.legado.app.ui.book.read.ReadBookUiState
 import io.legado.app.ui.book.readaloud.player.ReadAloudPlayerIntent
 import io.legado.app.ui.book.readaloud.player.ReadAloudPlayerIntent
 import io.legado.app.ui.book.readaloud.player.ReadAloudPlayerUiState
 import io.legado.app.ui.book.readaloud.player.ReadAloudPlayerUiState
-import io.legado.app.ui.widget.components.AppTextField
-import io.legado.app.ui.widget.components.alert.AppAlertDialog
-import io.legado.app.ui.widget.components.button.series.SmallTonalButton
-import io.legado.app.ui.widget.components.button.series.MediumTonalButton
-import io.legado.app.ui.widget.components.filePicker.FilePickerSheet
-import io.legado.app.ui.widget.components.importComponents.BatchImportDialog
-import io.legado.app.ui.widget.components.importComponents.SourceInputDialog
-import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu
-import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
 import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
 import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
 import io.legado.app.ui.widget.components.settingItem.SliderSettingItem
 import io.legado.app.ui.widget.components.settingItem.SliderSettingItem
 import io.legado.app.ui.widget.components.settingItem.TinyClickableSettingItem
 import io.legado.app.ui.widget.components.settingItem.TinyClickableSettingItem
 import io.legado.app.ui.widget.components.settingItem.TinyDropdownSettingItem
 import io.legado.app.ui.widget.components.settingItem.TinyDropdownSettingItem
 import io.legado.app.ui.widget.components.settingItem.TinySwitchSettingItem
 import io.legado.app.ui.widget.components.settingItem.TinySwitchSettingItem
 import io.legado.app.ui.widget.components.tabRow.CardTabRow
 import io.legado.app.ui.widget.components.tabRow.CardTabRow
-import io.legado.app.utils.GSON
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.launch
 
 
 @Composable
 @Composable
@@ -273,352 +245,6 @@ fun ReadAloudConfigContent(
     }
     }
 }
 }
 
 
-@Composable
-fun SpeakEngineConfigSheet(
-    show: Boolean,
-    state: ReadBookUiState,
-    onIntent: (ReadBookIntent) -> Unit,
-    onDismissRequest: () -> Unit,
-) {
-    val items = state.ttsEngineItems
-    val selectedValue = state.selectedTtsEngine
-    var pendingEngineSelection by remember { mutableStateOf<PendingSpeakEngineSelection?>(null) }
-    var showImportSheet by remember { mutableStateOf(false) }
-    var showUrlInput by remember { mutableStateOf(false) }
-
-    AppAlertDialog(
-        show = pendingEngineSelection != null,
-        onDismissRequest = { pendingEngineSelection = null },
-        title = stringResource(R.string.read_aloud_default_engine),
-        text = stringResource(R.string.speak_engine_apply_scope),
-        confirmText = stringResource(R.string.general),
-        onConfirm = {
-            onIntent(ReadBookIntent.ApplySpeakEngine(pendingEngineSelection?.value))
-            pendingEngineSelection = null
-        },
-        dismissText = stringResource(R.string.book),
-        onDismiss = {
-            onIntent(ReadBookIntent.ApplySpeakEnginePerBook(pendingEngineSelection?.value))
-            pendingEngineSelection = null
-        },
-    )
-    SourceInputDialog(
-        show = showUrlInput,
-        title = stringResource(R.string.import_on_line),
-        onDismissRequest = { showUrlInput = false },
-        onConfirm = {
-            showUrlInput = false
-            onIntent(ReadBookIntent.ImportHttpTtsSource(it))
-        },
-    )
-    FilePickerSheet(
-        show = showImportSheet,
-        onDismissRequest = { showImportSheet = false },
-        title = stringResource(R.string.import_tts),
-        onSelectSysFile = {
-            showImportSheet = false
-            onIntent(ReadBookIntent.ImportHttpTtsFile)
-        },
-        onManualInput = {
-            showImportSheet = false
-            showUrlInput = true
-        },
-        allowExtensions = arrayOf("json", "txt"),
-    )
-    BatchImportDialog(
-        title = stringResource(R.string.import_tts),
-        importState = state.httpTtsImportState,
-        onDismissRequest = { onIntent(ReadBookIntent.CancelHttpTtsImport) },
-        onToggleItem = { onIntent(ReadBookIntent.ToggleHttpTtsImportSelection(it)) },
-        onToggleAll = { onIntent(ReadBookIntent.ToggleHttpTtsImportAll(it)) },
-        onUpdateItem = { index, httpTTS ->
-            onIntent(ReadBookIntent.UpdateHttpTtsImportItem(index, httpTTS))
-        },
-        onConfirm = { onIntent(ReadBookIntent.SaveImportedHttpTts) },
-        itemTitle = { it.name },
-        itemSubtitle = { it.url },
-    )
-
-    AppModalBottomSheet(
-        show = show,
-        onDismissRequest = onDismissRequest,
-        title = stringResource(R.string.read_aloud_default_engine),
-        startAction = {
-            MediumTonalButton(
-                onClick = { onIntent(ReadBookIntent.EditHttpTts()) },
-                icon = Icons.Default.Add,
-                contentDescription = stringResource(R.string.add)
-            )
-        },
-        endAction = {
-            var expanded by remember { mutableStateOf(false) }
-            Box {
-                MediumTonalButton(
-                    onClick = { expanded = true },
-                    icon = Icons.Default.MoreVert,
-                    contentDescription = stringResource(R.string.more_menu)
-                )
-                RoundDropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) {
-                    RoundDropdownMenuItem(
-                        text = stringResource(R.string.import_tts),
-                        onClick = {
-                            expanded = false
-                            showImportSheet = true
-                        },
-                    )
-                    RoundDropdownMenuItem(
-                        text = stringResource(R.string.export),
-                        onClick = {
-                            expanded = false
-                            onIntent(ReadBookIntent.ExportAllHttpTts)
-                        },
-                    )
-                    RoundDropdownMenuItem(
-                        text = stringResource(R.string.copy_url),
-                        onClick = {
-                            expanded = false
-                            onIntent(ReadBookIntent.ExportAllHttpTtsAsUrl)
-                        },
-                    )
-                    RoundDropdownMenuItem(
-                        text = stringResource(R.string.clear_cache),
-                        onClick = {
-                            expanded = false
-                            onIntent(ReadBookIntent.ClearTtsCache)
-                        },
-                    )
-                }
-            }
-        },
-    ) {
-        LazyColumn(
-            modifier = Modifier
-                .fillMaxWidth()
-                .weight(1f, fill = false)
-                .padding(bottom = 8.dp),
-        ) {
-            items(items) { item ->
-                val httpTtsId = item.value?.toLongOrNull()
-                val isSelected = item.value == selectedValue
-                TinyClickableSettingItem(
-                    title = item.title,
-                    description = if (isSelected) {
-                        stringResource(R.string.read_aloud_current_default_summary)
-                    } else {
-                        null
-                    },
-                    onClick = { pendingEngineSelection = PendingSpeakEngineSelection(item.value) },
-                    onLongClick = if (httpTtsId != null && !item.loginUrl.isNullOrBlank()) {
-                        { onIntent(ReadBookIntent.OpenHttpTtsLogin(httpTtsId)) }
-                    } else null,
-                    trailingContent = if (httpTtsId != null) {
-                        {
-                            Row(horizontalArrangement = Arrangement.spacedBy(2.dp)) {
-                                SmallTonalButton(
-                                    onClick = { onIntent(ReadBookIntent.EditHttpTts(httpTtsId)) },
-                                    icon = Icons.Default.Edit,
-                                    contentDescription = stringResource(R.string.edit),
-                                )
-                                SmallTonalButton(
-                                    onClick = { onIntent(ReadBookIntent.DeleteHttpTts(httpTtsId)) },
-                                    icon = Icons.Default.Delete,
-                                    contentDescription = stringResource(R.string.delete),
-                                )
-                            }
-                        }
-                    } else null,
-                )
-            }
-        }
-    }
-}
-
-private data class PendingSpeakEngineSelection(val value: String?)
-
-@Composable
-fun HttpTtsEditSheet(
-    show: Boolean,
-    httpTTS: HttpTTS?,
-    onIntent: (ReadBookIntent) -> Unit,
-    onDismissRequest: () -> Unit,
-) {
-    val tts = httpTTS ?: return
-    val clipboardManager = LocalClipboardManager.current
-    var name by remember(httpTTS) { mutableStateOf(tts.name) }
-    var url by remember(httpTTS) { mutableStateOf(tts.url) }
-    var contentType by remember(httpTTS) { mutableStateOf(tts.contentType ?: "") }
-    var concurrentRate by remember(httpTTS) { mutableStateOf(tts.concurrentRate ?: "0") }
-    var header by remember(httpTTS) { mutableStateOf(tts.header ?: "") }
-    var loginUrl by remember(httpTTS) { mutableStateOf(tts.loginUrl ?: "") }
-    var loginUi by remember(httpTTS) { mutableStateOf(tts.loginUi ?: "") }
-    var loginCheckJs by remember(httpTTS) { mutableStateOf(tts.loginCheckJs ?: "") }
-    var jsLib by remember(httpTTS) { mutableStateOf(tts.jsLib ?: "") }
-
-    AppModalBottomSheet(
-        show = show,
-        onDismissRequest = onDismissRequest,
-        title = stringResource(R.string.speak_engine),
-        startAction = {
-            var expanded by remember { mutableStateOf(false) }
-            Box {
-                MediumTonalButton(
-                    onClick = { expanded = true },
-                    icon = Icons.Default.MoreVert,
-                    contentDescription = stringResource(R.string.more_menu)
-                )
-                RoundDropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) {
-                    RoundDropdownMenuItem(
-                        text = stringResource(R.string.copy_text),
-                        onClick = {
-                            expanded = false
-                            val json = GSON.toJson(
-                                tts.copy(
-                                    name = name, url = url, contentType = contentType,
-                                    concurrentRate = concurrentRate, header = header,
-                                    loginUrl = loginUrl, loginUi = loginUi,
-                                    loginCheckJs = loginCheckJs, jsLib = jsLib,
-                                )
-                            )
-                            clipboardManager.setText(AnnotatedString(json))
-                        },
-                    )
-                    RoundDropdownMenuItem(
-                        text = stringResource(R.string.paste_source),
-                        onClick = {
-                            expanded = false
-                            clipboardManager.getText()?.text?.let { text ->
-                                HttpTTS.fromJson(text).getOrNull()?.let { imported ->
-                                    name = imported.name
-                                    url = imported.url
-                                    contentType = imported.contentType ?: ""
-                                    concurrentRate = imported.concurrentRate ?: "0"
-                                    header = imported.header ?: ""
-                                    loginUrl = imported.loginUrl ?: ""
-                                    loginUi = imported.loginUi ?: ""
-                                    loginCheckJs = imported.loginCheckJs ?: ""
-                                    jsLib = imported.jsLib ?: ""
-                                }
-                            }
-                        },
-                    )
-                }
-            }
-        },
-        endAction = {
-            MediumTonalButton(
-                icon = Icons.Default.Save,
-                contentDescription = stringResource(R.string.save),
-                onClick = {
-                    onIntent(
-                        ReadBookIntent.SaveHttpTts(
-                            tts.copy(
-                                name = name, url = url,
-                                contentType = contentType.ifBlank { null },
-                                concurrentRate = concurrentRate,
-                                header = header.ifBlank { null },
-                                loginUrl = loginUrl.ifBlank { null },
-                                loginUi = loginUi.ifBlank { null },
-                                loginCheckJs = loginCheckJs.ifBlank { null },
-                                jsLib = jsLib.ifBlank { null },
-                            )
-                        )
-                    )
-                },
-            )
-        },
-    ) {
-        LazyColumn(
-            modifier = Modifier
-                .fillMaxWidth()
-                .padding(vertical = 8.dp),
-            verticalArrangement = Arrangement.spacedBy(12.dp),
-        ) {
-            item {
-                AppTextField(
-                    value = name,
-                    onValueChange = { name = it },
-                    label = stringResource(R.string.name),
-                    singleLine = true,
-                    modifier = Modifier.fillMaxWidth(),
-                )
-            }
-            item {
-                AppTextField(
-                    value = url,
-                    onValueChange = { url = it },
-                    label = "URL",
-                    minLines = 2,
-                    modifier = Modifier.fillMaxWidth(),
-                )
-            }
-            item {
-                AppTextField(
-                    value = contentType,
-                    onValueChange = { contentType = it },
-                    label = "Content-Type",
-                    singleLine = true,
-                    modifier = Modifier.fillMaxWidth(),
-                )
-            }
-            item {
-                AppTextField(
-                    value = concurrentRate,
-                    onValueChange = { concurrentRate = it },
-                    label = stringResource(R.string.concurrent_rate),
-                    singleLine = true,
-                    modifier = Modifier.fillMaxWidth(),
-                )
-            }
-            item {
-                AppTextField(
-                    value = header,
-                    onValueChange = { header = it },
-                    label = stringResource(R.string.source_http_header),
-                    minLines = 2,
-                    modifier = Modifier.fillMaxWidth(),
-                )
-            }
-            item {
-                AppTextField(
-                    value = loginUrl,
-                    onValueChange = { loginUrl = it },
-                    label = stringResource(R.string.login_url),
-                    singleLine = true,
-                    modifier = Modifier.fillMaxWidth(),
-                )
-            }
-            item {
-                AppTextField(
-                    value = loginUi,
-                    onValueChange = { loginUi = it },
-                    label = stringResource(R.string.login_ui),
-                    minLines = 2,
-                    modifier = Modifier.fillMaxWidth(),
-                )
-            }
-            item {
-                AppTextField(
-                    value = loginCheckJs,
-                    onValueChange = { loginCheckJs = it },
-                    label = stringResource(R.string.login_check_js),
-                    minLines = 2,
-                    modifier = Modifier.fillMaxWidth(),
-                )
-            }
-            item {
-                AppTextField(
-                    value = jsLib,
-                    onValueChange = { jsLib = it },
-                    label = "jsLib",
-                    minLines = 2,
-                    modifier = Modifier.fillMaxWidth(),
-                )
-            }
-            item { Spacer(Modifier.height(16.dp)) }
-        }
-    }
-}
-
 @Composable
 @Composable
 fun ReadAloudNumberConfigSheet(
 fun ReadAloudNumberConfigSheet(
     show: Boolean,
     show: Boolean,