From ed3b93048f795bb4786835a564cd61ed5f4f3998 Mon Sep 17 00:00:00 2001 From: Jing <42014615+jing332@users.noreply.github.com> Date: Wed, 2 Aug 2023 10:38:17 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E5=AF=BC=E5=87=BAyam?= =?UTF-8?q?l?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 1 + .../jing332/tts_dict_editor/const/AppConst.kt | 13 +++++ .../tts_dict_editor/const/ExportType.kt | 7 +++ .../tts_dict_editor/replace/ReplaceConfig.kt | 10 ++++ .../tts_dict_editor/replace/ReplaceManager.kt | 58 +++++++++++++++++++ .../replace/ReplaceRuleYaml.kt | 12 ++++ .../tts_dict_editor/ui/MainActivity.kt | 1 - .../ui/replace/ConfigExportUiMenu.kt | 16 ++++- .../tts_dict_editor/ui/replace/GroupItem.kt | 14 +++-- .../ui/replace/ReplaceRuleActivity.kt | 2 +- .../ui/replace/RuleManagerScreen.kt | 44 +++++++++----- .../ui/replace/RuleManagerViewModel.kt | 20 ++++++- .../ui/replace/edit/RuleEditScreen.kt | 1 - .../jing332/tts_dict_editor/ui/theme/Theme.kt | 1 + app/src/main/res/values/strings.xml | 2 + 15 files changed, 176 insertions(+), 26 deletions(-) create mode 100644 app/src/main/java/com/github/jing332/tts_dict_editor/const/ExportType.kt create mode 100644 app/src/main/java/com/github/jing332/tts_dict_editor/replace/ReplaceConfig.kt create mode 100644 app/src/main/java/com/github/jing332/tts_dict_editor/replace/ReplaceManager.kt create mode 100644 app/src/main/java/com/github/jing332/tts_dict_editor/replace/ReplaceRuleYaml.kt diff --git a/app/build.gradle b/app/build.gradle index 3c92c66..51b22ea 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -130,6 +130,7 @@ dependencies { implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.1' implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1' + implementation("com.charleskorn.kaml:kaml:0.55.0") implementation 'androidx.core:core-ktx:1.10.1' implementation 'androidx.activity:activity-compose:1.7.2' diff --git a/app/src/main/java/com/github/jing332/tts_dict_editor/const/AppConst.kt b/app/src/main/java/com/github/jing332/tts_dict_editor/const/AppConst.kt index 2a1d1e6..4ef37a3 100644 --- a/app/src/main/java/com/github/jing332/tts_dict_editor/const/AppConst.kt +++ b/app/src/main/java/com/github/jing332/tts_dict_editor/const/AppConst.kt @@ -1,5 +1,8 @@ package com.github.jing332.tts_dict_editor.const +import com.charleskorn.kaml.PolymorphismStyle +import com.charleskorn.kaml.Yaml +import com.charleskorn.kaml.YamlConfiguration import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.Json @@ -12,4 +15,14 @@ object AppConst { explicitNulls = false } } + + val yaml by lazy { + Yaml( + configuration = YamlConfiguration( + encodeDefaults = false, + strictMode = false, + polymorphismStyle = PolymorphismStyle.Tag + ) + ) + } } \ No newline at end of file diff --git a/app/src/main/java/com/github/jing332/tts_dict_editor/const/ExportType.kt b/app/src/main/java/com/github/jing332/tts_dict_editor/const/ExportType.kt new file mode 100644 index 0000000..ae3d081 --- /dev/null +++ b/app/src/main/java/com/github/jing332/tts_dict_editor/const/ExportType.kt @@ -0,0 +1,7 @@ +package com.github.jing332.tts_dict_editor.const + +object ExportType { + const val JSON = 0 + const val CUSTOM_FORMAT = 1 + const val YAML = 2 +} \ No newline at end of file diff --git a/app/src/main/java/com/github/jing332/tts_dict_editor/replace/ReplaceConfig.kt b/app/src/main/java/com/github/jing332/tts_dict_editor/replace/ReplaceConfig.kt new file mode 100644 index 0000000..1290d9c --- /dev/null +++ b/app/src/main/java/com/github/jing332/tts_dict_editor/replace/ReplaceConfig.kt @@ -0,0 +1,10 @@ +package com.github.jing332.tts_dict_editor.replace + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +@SerialName("!org.nobody.multitts.tts.replace.ReplaceConfig") +data class ReplaceConfig( + val replaces: List +) \ No newline at end of file diff --git a/app/src/main/java/com/github/jing332/tts_dict_editor/replace/ReplaceManager.kt b/app/src/main/java/com/github/jing332/tts_dict_editor/replace/ReplaceManager.kt new file mode 100644 index 0000000..b30a4bc --- /dev/null +++ b/app/src/main/java/com/github/jing332/tts_dict_editor/replace/ReplaceManager.kt @@ -0,0 +1,58 @@ +package com.github.jing332.tts_dict_editor.replace + +import android.content.Context +import android.net.Uri +import com.charleskorn.kaml.decodeFromStream +import com.charleskorn.kaml.encodeToStream +import com.github.jing332.tts_dict_editor.const.AppConst +import kotlinx.serialization.encodeToString +import java.io.InputStream + +class ReplaceManager(val context: Context, val uri: Uri) { + companion object { + const val TAG = "ReplaceManager" + + fun toYaml(list: List): String { + return AppConst.yaml.encodeToString(ReplaceConfig(list)) + } + } + + private val replaceList = mutableListOf() + + fun addReplace(replaceConfig: ReplaceRuleYaml) { + replaceList.add(replaceConfig) + } + + fun getReplaceList(): List { + return replaceList + } + + fun clearReplaceList() { + replaceList.clear() + } + + fun readConfigFromFile() { + context.contentResolver.openInputStream(uri)?.use { + readConfigFromInputStream(it) + return + } + + throw Exception("Failed to read config file") + } + + private fun readConfigFromInputStream(ins: InputStream) { + val cfg: ReplaceConfig = AppConst.yaml.decodeFromStream(ins) + replaceList.clear() + replaceList.addAll(cfg.replaces) + } + + private fun updateConfigFile() { + val cfg = ReplaceConfig(replaceList) + context.contentResolver.openOutputStream(uri)?.use { + AppConst.yaml.encodeToStream(cfg, it) + return + } + + throw Exception("Failed to write config file") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/jing332/tts_dict_editor/replace/ReplaceRuleYaml.kt b/app/src/main/java/com/github/jing332/tts_dict_editor/replace/ReplaceRuleYaml.kt new file mode 100644 index 0000000..9ec1eb0 --- /dev/null +++ b/app/src/main/java/com/github/jing332/tts_dict_editor/replace/ReplaceRuleYaml.kt @@ -0,0 +1,12 @@ +package com.github.jing332.tts_dict_editor.replace + +import kotlinx.serialization.Serializable + +@Serializable +data class ReplaceRuleYaml( + val activate: Boolean = false, + val name: String = "", + val regex: Boolean = false, + val source: String, + val target: String +) \ No newline at end of file diff --git a/app/src/main/java/com/github/jing332/tts_dict_editor/ui/MainActivity.kt b/app/src/main/java/com/github/jing332/tts_dict_editor/ui/MainActivity.kt index 7c5f369..746effe 100644 --- a/app/src/main/java/com/github/jing332/tts_dict_editor/ui/MainActivity.kt +++ b/app/src/main/java/com/github/jing332/tts_dict_editor/ui/MainActivity.kt @@ -83,7 +83,6 @@ class MainActivity : ComponentActivity() { } - @OptIn(ExperimentalAnimationApi::class) @Composable fun AppNavigation( navController: NavHostController = rememberNavController(), diff --git a/app/src/main/java/com/github/jing332/tts_dict_editor/ui/replace/ConfigExportUiMenu.kt b/app/src/main/java/com/github/jing332/tts_dict_editor/ui/replace/ConfigExportUiMenu.kt index c0ed6d0..cab78c1 100644 --- a/app/src/main/java/com/github/jing332/tts_dict_editor/ui/replace/ConfigExportUiMenu.kt +++ b/app/src/main/java/com/github/jing332/tts_dict_editor/ui/replace/ConfigExportUiMenu.kt @@ -14,7 +14,11 @@ import me.saket.cascade.CascadeColumnScope @Composable -internal fun CascadeColumnScope.ConfigExportUiMenu(onJson: () -> Unit, onCustomFormat: () -> Unit) { +internal fun CascadeColumnScope.ConfigExportUiMenu( + onJson: () -> Unit, + onCustomFormat: () -> Unit, + onYamlFormat: () -> Unit +) { DropdownMenuItem(leadingIcon = { Icon( Icons.Filled.Output, "", @@ -30,6 +34,16 @@ internal fun CascadeColumnScope.ConfigExportUiMenu(onJson: () -> Unit, onCustomF }, text = { Text(stringResource(R.string.json_format)) }, onClick = { onJson.invoke() }) + androidx.compose.material3.DropdownMenuItem( + leadingIcon = { + Icon( + Icons.Filled.FormatColorText, "", + tint = MaterialTheme.colorScheme.onBackground + ) + }, + text = { Text(stringResource(R.string.new_multitts_format)) }, + onClick = { onYamlFormat() }) + androidx.compose.material3.DropdownMenuItem( leadingIcon = { Icon( diff --git a/app/src/main/java/com/github/jing332/tts_dict_editor/ui/replace/GroupItem.kt b/app/src/main/java/com/github/jing332/tts_dict_editor/ui/replace/GroupItem.kt index a925bd2..9609943 100644 --- a/app/src/main/java/com/github/jing332/tts_dict_editor/ui/replace/GroupItem.kt +++ b/app/src/main/java/com/github/jing332/tts_dict_editor/ui/replace/GroupItem.kt @@ -28,6 +28,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import com.github.jing332.tts_dict_editor.R +import com.github.jing332.tts_dict_editor.const.ExportType import me.saket.cascade.CascadeDropdownMenu import me.saket.cascade.rememberCascadeState @@ -39,7 +40,7 @@ fun GroupItem( onClick: () -> Unit, onEdit: () -> Unit, onDeleteAction: () -> Unit, - onExportAction: (isCustomType: Boolean) -> Unit, + onExportAction: (type: Int) -> Unit, ) { Row( modifier @@ -90,9 +91,14 @@ fun GroupItem( } ) - ConfigExportUiMenu(onJson = { onExportAction.invoke(false) }) { - onExportAction.invoke(true) // Custom Format - } + ConfigExportUiMenu(onJson = { onExportAction.invoke(ExportType.JSON) }, + onCustomFormat = { + onExportAction.invoke(ExportType.CUSTOM_FORMAT) // Custom Format + }, + onYamlFormat = { + onExportAction(ExportType.YAML) + } + ) Divider( Modifier diff --git a/app/src/main/java/com/github/jing332/tts_dict_editor/ui/replace/ReplaceRuleActivity.kt b/app/src/main/java/com/github/jing332/tts_dict_editor/ui/replace/ReplaceRuleActivity.kt index 175b9ed..8a961fb 100644 --- a/app/src/main/java/com/github/jing332/tts_dict_editor/ui/replace/ReplaceRuleActivity.kt +++ b/app/src/main/java/com/github/jing332/tts_dict_editor/ui/replace/ReplaceRuleActivity.kt @@ -87,7 +87,7 @@ class ReplaceRuleActivity : ComponentActivity() { contentResolver.openOutputStream(uri, "wt" /*覆写*/) ?.use { os -> os.write(txt.toByteArray()) } }.onFailure { t -> -// errDialog = "保存文件错误" to t + longToast(getString(R.string.failed_to_save) + ": " + t.message) } } } diff --git a/app/src/main/java/com/github/jing332/tts_dict_editor/ui/replace/RuleManagerScreen.kt b/app/src/main/java/com/github/jing332/tts_dict_editor/ui/replace/RuleManagerScreen.kt index 5bf0697..f836635 100644 --- a/app/src/main/java/com/github/jing332/tts_dict_editor/ui/replace/RuleManagerScreen.kt +++ b/app/src/main/java/com/github/jing332/tts_dict_editor/ui/replace/RuleManagerScreen.kt @@ -32,7 +32,6 @@ import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults -import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -48,6 +47,7 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.github.jing332.tts_dict_editor.R import com.github.jing332.tts_dict_editor.const.AppConst +import com.github.jing332.tts_dict_editor.const.ExportType import com.github.jing332.tts_dict_editor.help.AppConfig import com.github.jing332.tts_dict_editor.help.GroupWithReplaceRule import com.github.jing332.tts_dict_editor.help.ReplaceRule @@ -218,6 +218,12 @@ fun ReplaceRuleManagerScreen( onCustomFormat = { isMoreOptionsVisible = false isVisibleCustomFormatExport = true to null + }, + onYamlFormat = { + isMoreOptionsVisible = false + coroutineScope.launch { + exportedConfigText = vm.exportYaml() + } } ) } @@ -245,18 +251,21 @@ fun ReplaceRuleManagerScreen( onEditRule = { onEditRule.invoke(it) }, onDeleteRule = { coroutineScope.launch { vm.deleteRule(it) } }, onReorder = { from, to -> vm.reorder(from, to) }, - onExportGroup = { group, isCustomFormat -> - if (isCustomFormat) - isVisibleCustomFormatExport = true to group - else - coroutineScope.launch { - exportedConfigText = - com.drake.net.utils.withDefault { vm.exportGroup(group) } + onExportGroup = { group, type -> + coroutineScope.launch { + when (type) { + ExportType.JSON -> exportedConfigText = vm.exportGroup(group) + ExportType.CUSTOM_FORMAT -> isVisibleCustomFormatExport = + true to group + + ExportType.YAML -> exportedConfigText = vm.exportYaml() } + } }, ) } - }) + } + ) if (isVisibleImportConfig) ImportConfigDialog( @@ -293,8 +302,13 @@ fun ReplaceRuleManagerScreen( isVisibleCustomFormatExport = false to null coroutineScope.launch { exportedConfigText = - if (isVisibleCustomFormatExport.second == null) vm.exportByFormat(format) - else vm.exportGroupByFormat(isVisibleCustomFormatExport.second!!, format) + if (isVisibleCustomFormatExport.second == null) vm.exportByFormat( + format + ) + else vm.exportGroupByFormat( + isVisibleCustomFormatExport.second!!, + format + ) } } ) @@ -308,7 +322,7 @@ private fun ImportConfigDialog( onDismissRequest: () -> Unit, onImport: (List) -> Unit ) { - val state = rememberModalBottomSheetState(skipPartiallyExpanded = true) +// val state = rememberModalBottomSheetState(skipPartiallyExpanded = true) var selectDialogData by remember { mutableStateOf?>(null) } @@ -337,7 +351,7 @@ private fun Screen( onEditRule: (ReplaceRule) -> Unit, onDeleteRule: (ReplaceRule) -> Unit, onReorder: (fromIndex: Int, toIndex: Int) -> Unit, - onExportGroup: (ReplaceRuleGroup, Boolean) -> Unit, + onExportGroup: (ReplaceRuleGroup, Int) -> Unit, ) { val orderState = rememberReorderableLazyListState(onMove = { from, to -> println("from $from to $to") @@ -350,7 +364,7 @@ private fun Screen( .reorderable(orderState) .detectReorderAfterLongPress(orderState) ) { - list.forEachIndexed { index, item -> + list.forEachIndexed { _, item -> when (item) { is ReplaceRuleGroup -> { stickyHeader(key = "group_${item.id}") { @@ -453,7 +467,7 @@ fun PreviewOrderList() { .detectReorderAfterLongPress(orderState), ) { items(list, key = { it }) { - ReorderableItem(state = orderState, key = it) { isDraging -> + ReorderableItem(state = orderState, key = it) { _ -> Text( text = it, modifier = Modifier diff --git a/app/src/main/java/com/github/jing332/tts_dict_editor/ui/replace/RuleManagerViewModel.kt b/app/src/main/java/com/github/jing332/tts_dict_editor/ui/replace/RuleManagerViewModel.kt index 170557b..ee08e0f 100644 --- a/app/src/main/java/com/github/jing332/tts_dict_editor/ui/replace/RuleManagerViewModel.kt +++ b/app/src/main/java/com/github/jing332/tts_dict_editor/ui/replace/RuleManagerViewModel.kt @@ -13,6 +13,8 @@ import com.github.jing332.tts_dict_editor.help.DictFileManager.Companion.toTxt import com.github.jing332.tts_dict_editor.help.GroupWithReplaceRule import com.github.jing332.tts_dict_editor.help.ReplaceRule import com.github.jing332.tts_dict_editor.help.ReplaceRuleGroup +import com.github.jing332.tts_dict_editor.replace.ReplaceManager +import com.github.jing332.tts_dict_editor.replace.ReplaceRuleYaml import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.isActive @@ -168,6 +170,18 @@ class RuleManagerViewModel : ViewModel() { return count } + suspend fun exportYaml(): String { + return ReplaceManager.toYaml(rules().map { + ReplaceRuleYaml( + activate = it.isEnabled, + name = it.name.ifEmpty { it.pattern }, + regex = it.isRegex, + source = it.pattern, + target = it.replacement + ) + }) + } + suspend fun export(): String { val gwrs = groupWithRules() return AppConst.json.encodeToString(gwrs) @@ -184,7 +198,7 @@ class RuleManagerViewModel : ViewModel() { return AppConst.json.encodeToString(gwrs) } - suspend fun exportGroupByFormat(group: ReplaceRuleGroup, format:String):String{ + suspend fun exportGroupByFormat(group: ReplaceRuleGroup, format: String): String { val gwrs = groupWithRules().filter { it.group.id == group.id } return gwrs.flatMap { it.list } .joinToString("\n") { format.replace("$1", it.pattern).replace("$2", it.replacement) } @@ -193,8 +207,8 @@ class RuleManagerViewModel : ViewModel() { fun reorder(from: Int, to: Int) { val fromItem = list[from] val toItem = list[to] - val startIndex = min(from, to) - val endIndex = max(from, to) +// val startIndex = min(from, to) +// val endIndex = max(from, to) if (fromItem is ReplaceRule && toItem is ReplaceRule && fromItem.groupId == toItem.groupId) { list.add(to, list.removeAt(from)) diff --git a/app/src/main/java/com/github/jing332/tts_dict_editor/ui/replace/edit/RuleEditScreen.kt b/app/src/main/java/com/github/jing332/tts_dict_editor/ui/replace/edit/RuleEditScreen.kt index 4122e31..36b31be 100644 --- a/app/src/main/java/com/github/jing332/tts_dict_editor/ui/replace/edit/RuleEditScreen.kt +++ b/app/src/main/java/com/github/jing332/tts_dict_editor/ui/replace/edit/RuleEditScreen.kt @@ -94,7 +94,6 @@ fun RuleEditScreen( onResult: (ReplaceRule?) -> Unit, ) { val vm: RuleEditViewModel = viewModel() - var isVisibleToolbar by remember { mutableStateOf(false) } var inputKeyState = remember { mutableStateOf("") } var toolbarKeyList: List> by rememberDataSaverState( key = ConfigConst.KEY_SOFT_KEYBOARD_TOOLBAR, diff --git a/app/src/main/java/com/github/jing332/tts_dict_editor/ui/theme/Theme.kt b/app/src/main/java/com/github/jing332/tts_dict_editor/ui/theme/Theme.kt index 1ab855c..b555bbe 100644 --- a/app/src/main/java/com/github/jing332/tts_dict_editor/ui/theme/Theme.kt +++ b/app/src/main/java/com/github/jing332/tts_dict_editor/ui/theme/Theme.kt @@ -85,6 +85,7 @@ fun getAppTheme(): AppTheme = themeTypeState.value /** * 根Context */ +@Suppress("DEPRECATION") @Composable fun DictEditorTheme( modifier: Modifier = Modifier, diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 106a2a2..66fa622 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -69,4 +69,6 @@ 灰色 主题 您的系统不支持动态主题(需Android12及以上) + 新·MultiTTS + 保存失败:%s \ No newline at end of file