diff --git a/app/src/main/kotlin/com/github/kr328/clash/crash/ui/ApkBrokenScreen.kt b/app/src/main/kotlin/com/github/kr328/clash/crash/ui/ApkBrokenScreen.kt index be584b0ac4..eb01c69844 100644 --- a/app/src/main/kotlin/com/github/kr328/clash/crash/ui/ApkBrokenScreen.kt +++ b/app/src/main/kotlin/com/github/kr328/clash/crash/ui/ApkBrokenScreen.kt @@ -15,8 +15,8 @@ import com.github.kr328.clash.main.ui.openLink import com.github.kr328.clash.ui.component.MihomoScaffold import com.github.kr328.clash.ui.theme.MihomoTheme import com.github.kr328.clash.ui.theme.PreviewMihomo -import me.zhanghai.compose.preference.Preference import me.zhanghai.compose.preference.ProvidePreferenceLocals +import me.zhanghai.compose.preference.preference import me.zhanghai.compose.preference.preferenceCategory @OptIn(ExperimentalMaterial3Api::class) @@ -26,26 +26,24 @@ fun ApkBrokenScreen() { ProvidePreferenceLocals { val context = LocalContext.current LazyColumn(modifier = Modifier.fillMaxSize(), contentPadding = innerPadding) { - item(key = "tips_application_broken") { - Preference( - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.application_broken)) }, - summary = { Text(stringResource(R.string.application_broken_tips)) }, - enabled = false, - ) - } + preference( + key = "tips_application_broken", + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.application_broken)) }, + summary = { Text(stringResource(R.string.application_broken_tips)) }, + enabled = false, + ) preferenceCategory( key = "cat_reinstall", title = { Text(stringResource(R.string.reinstall)) }, ) - item(key = "github_releases") { - Preference( - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.github_releases)) }, - summary = { Text(CMFA_GITHUB) }, - onClick = { context.openLink(CMFA_GITHUB) }, - ) - } + preference( + key = "github_releases", + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.github_releases)) }, + summary = { Text(CMFA_GITHUB) }, + onClick = { context.openLink(CMFA_GITHUB) }, + ) } } } diff --git a/app/src/main/kotlin/com/github/kr328/clash/main/ui/HelpScreen.kt b/app/src/main/kotlin/com/github/kr328/clash/main/ui/HelpScreen.kt index 2b842b3c97..a576a2d47f 100644 --- a/app/src/main/kotlin/com/github/kr328/clash/main/ui/HelpScreen.kt +++ b/app/src/main/kotlin/com/github/kr328/clash/main/ui/HelpScreen.kt @@ -18,8 +18,8 @@ import com.github.kr328.clash.R import com.github.kr328.clash.ui.component.MihomoScaffold import com.github.kr328.clash.ui.theme.MihomoTheme import com.github.kr328.clash.ui.theme.PreviewMihomo -import me.zhanghai.compose.preference.Preference import me.zhanghai.compose.preference.ProvidePreferenceLocals +import me.zhanghai.compose.preference.preference import me.zhanghai.compose.preference.preferenceCategory @OptIn(ExperimentalMaterial3Api::class) @@ -29,51 +29,46 @@ fun HelpScreen(modifier: Modifier = Modifier) { ProvidePreferenceLocals { val context = LocalContext.current LazyColumn(modifier = Modifier.fillMaxSize(), contentPadding = innerPadding) { - item(key = "tips_help") { - Preference( - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.help)) }, - summary = { Text(AnnotatedString.fromHtml(stringResource(R.string.tips_help))) }, - enabled = false, - ) - } + preference( + key = "tips_help", + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.help)) }, + summary = { Text(AnnotatedString.fromHtml(stringResource(R.string.tips_help))) }, + enabled = false, + ) preferenceCategory( key = "cat_document", title = { Text(stringResource(R.string.document)) }, ) - item(key = "clash_wiki") { - Preference( - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.clash_wiki)) }, - summary = { Text(CLASH_WIKI) }, - onClick = { context.openLink(CLASH_WIKI) }, - ) - } - item(key = "clash_meta_wiki") { - Preference( - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.clash_meta_wiki)) }, - summary = { Text(CLASH_META_WIKI) }, - onClick = { context.openLink(CLASH_META_WIKI) }, - ) - } + preference( + key = "clash_wiki", + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.clash_wiki)) }, + summary = { Text(CLASH_WIKI) }, + onClick = { context.openLink(CLASH_WIKI) }, + ) + preference( + key = "clash_meta_wiki", + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.clash_meta_wiki)) }, + summary = { Text(CLASH_META_WIKI) }, + onClick = { context.openLink(CLASH_META_WIKI) }, + ) preferenceCategory(key = "cat_sources", title = { Text(stringResource(R.string.sources)) }) - item(key = "clash_meta_core") { - Preference( - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.clash_meta_core)) }, - summary = { Text(CLASH_META_CORE) }, - onClick = { context.openLink(CLASH_META_CORE) }, - ) - } - item(key = "clash_meta_for_android") { - Preference( - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.clash_meta_for_android)) }, - summary = { Text(CMFA_GITHUB) }, - onClick = { context.openLink(CMFA_GITHUB) }, - ) - } + preference( + key = "clash_meta_core", + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.clash_meta_core)) }, + summary = { Text(CLASH_META_CORE) }, + onClick = { context.openLink(CLASH_META_CORE) }, + ) + preference( + key = "clash_meta_for_android", + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.clash_meta_for_android)) }, + summary = { Text(CMFA_GITHUB) }, + onClick = { context.openLink(CMFA_GITHUB) }, + ) } } } diff --git a/app/src/main/kotlin/com/github/kr328/clash/settings/ui/AppSettingsScreen.kt b/app/src/main/kotlin/com/github/kr328/clash/settings/ui/AppSettingsScreen.kt index 60cfe36134..8b45f4583e 100644 --- a/app/src/main/kotlin/com/github/kr328/clash/settings/ui/AppSettingsScreen.kt +++ b/app/src/main/kotlin/com/github/kr328/clash/settings/ui/AppSettingsScreen.kt @@ -24,10 +24,10 @@ import com.github.kr328.clash.ui.icon.BaselineStack import com.github.kr328.clash.ui.icon.MihomoIcons import com.github.kr328.clash.ui.theme.MihomoTheme import com.github.kr328.clash.ui.theme.PreviewMihomo -import me.zhanghai.compose.preference.ListPreference import me.zhanghai.compose.preference.ProvidePreferenceLocals -import me.zhanghai.compose.preference.SwitchPreference +import me.zhanghai.compose.preference.listPreference import me.zhanghai.compose.preference.preferenceCategory +import me.zhanghai.compose.preference.switchPreference @Composable fun AppSettingsScreen( @@ -69,65 +69,56 @@ private fun AppSettingsContent( key = "cat_behavior", title = { Text(stringResource(R.string.behavior)) }, ) - item(key = "auto_restart") { - SwitchPreference( - value = uiState.autoRestart, - onValueChange = onAutoRestartChange, - icon = { Icon(imageVector = MihomoIcons.BaselineRestore, contentDescription = null) }, - title = { Text(stringResource(R.string.auto_restart)) }, - summary = { Text(stringResource(R.string.allow_clash_auto_restart)) }, - ) - } + switchPreference( + key = "auto_restart", + value = uiState.autoRestart, + onValueChange = onAutoRestartChange, + icon = { Icon(imageVector = MihomoIcons.BaselineRestore, contentDescription = null) }, + title = { Text(stringResource(R.string.auto_restart)) }, + summary = { Text(stringResource(R.string.allow_clash_auto_restart)) }, + ) preferenceCategory( key = "cat_interface", title = { Text(stringResource(R.string.interface_)) }, ) - item(key = "dark_mode") { - ListPreference( - value = uiState.darkMode, - onValueChange = onDarkModeChange, - values = listOf(DarkMode.Auto, DarkMode.ForceLight, DarkMode.ForceDark), - icon = { - Icon(imageVector = MihomoIcons.BaselineBrightness4, contentDescription = null) - }, - title = { Text(stringResource(R.string.dark_mode)) }, - summary = { Text(stringResource(uiState.darkMode.summaryRes)) }, - valueToText = { - androidx.compose.ui.text.AnnotatedString(stringResource(it.summaryRes)) - }, - ) - } - item(key = "hide_app_icon") { - SwitchPreference( - value = uiState.hideAppIcon, - onValueChange = onHideAppIconChange, - icon = { Icon(imageVector = MihomoIcons.BaselineHide, contentDescription = null) }, - title = { Text(stringResource(R.string.hide_app_icon_title)) }, - summary = { Text(stringResource(R.string.hide_app_icon_desc)) }, - ) - } - item(key = "hide_from_recents") { - SwitchPreference( - value = uiState.hideFromRecents, - onValueChange = onHideFromRecentsChange, - icon = { Icon(imageVector = MihomoIcons.BaselineStack, contentDescription = null) }, - title = { Text(stringResource(R.string.hide_from_recents_title)) }, - summary = { Text(stringResource(R.string.hide_from_recents_desc)) }, - ) - } + listPreference( + key = "dark_mode", + value = uiState.darkMode, + onValueChange = onDarkModeChange, + values = listOf(DarkMode.Auto, DarkMode.ForceLight, DarkMode.ForceDark), + icon = { Icon(imageVector = MihomoIcons.BaselineBrightness4, contentDescription = null) }, + title = { Text(stringResource(R.string.dark_mode)) }, + summary = { Text(stringResource(uiState.darkMode.summaryRes)) }, + valueToText = { androidx.compose.ui.text.AnnotatedString(stringResource(it.summaryRes)) }, + ) + switchPreference( + key = "hide_app_icon", + value = uiState.hideAppIcon, + onValueChange = onHideAppIconChange, + icon = { Icon(imageVector = MihomoIcons.BaselineHide, contentDescription = null) }, + title = { Text(stringResource(R.string.hide_app_icon_title)) }, + summary = { Text(stringResource(R.string.hide_app_icon_desc)) }, + ) + switchPreference( + key = "hide_from_recents", + value = uiState.hideFromRecents, + onValueChange = onHideFromRecentsChange, + icon = { Icon(imageVector = MihomoIcons.BaselineStack, contentDescription = null) }, + title = { Text(stringResource(R.string.hide_from_recents_title)) }, + summary = { Text(stringResource(R.string.hide_from_recents_desc)) }, + ) preferenceCategory(key = "cat_service", title = { Text(stringResource(R.string.service)) }) - item(key = "show_traffic") { - SwitchPreference( - value = uiState.dynamicNotification, - onValueChange = onDynamicNotificationChange, - enabled = !clashRunning, - icon = { Icon(imageVector = MihomoIcons.BaselineDomain, contentDescription = null) }, - title = { Text(stringResource(R.string.show_traffic)) }, - summary = { Text(stringResource(R.string.show_traffic_summary)) }, - ) - } + switchPreference( + key = "show_traffic", + value = uiState.dynamicNotification, + onValueChange = onDynamicNotificationChange, + enabled = !clashRunning, + icon = { Icon(imageVector = MihomoIcons.BaselineDomain, contentDescription = null) }, + title = { Text(stringResource(R.string.show_traffic)) }, + summary = { Text(stringResource(R.string.show_traffic_summary)) }, + ) } } } diff --git a/app/src/main/kotlin/com/github/kr328/clash/settings/ui/MetaFeatureSettingsScreen.kt b/app/src/main/kotlin/com/github/kr328/clash/settings/ui/MetaFeatureSettingsScreen.kt index 070bec98f0..ff023a8b4a 100644 --- a/app/src/main/kotlin/com/github/kr328/clash/settings/ui/MetaFeatureSettingsScreen.kt +++ b/app/src/main/kotlin/com/github/kr328/clash/settings/ui/MetaFeatureSettingsScreen.kt @@ -44,9 +44,9 @@ import com.github.kr328.clash.ui.icon.MihomoIcons import com.github.kr328.clash.ui.theme.MihomoTheme import com.github.kr328.clash.ui.theme.PreviewMihomo import kotlinx.serialization.Serializable -import me.zhanghai.compose.preference.ListPreference -import me.zhanghai.compose.preference.Preference import me.zhanghai.compose.preference.ProvidePreferenceLocals +import me.zhanghai.compose.preference.listPreference +import me.zhanghai.compose.preference.preference import me.zhanghai.compose.preference.preferenceCategory private sealed interface MetaFeatureSettingsRoute : NavKey { @@ -247,50 +247,46 @@ private fun LazyListScope.metaBasicPreferenceItems( ) { preferenceCategory(key = "cat_settings", title = { Text(stringResource(R.string.settings)) }) - item(key = "unifiedDelay", contentType = "ListPreference") { - ListPreference( - value = configuration.unifiedDelay, - onValueChange = actions::updateUnifiedDelay, - values = booleanOptions, - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.unified_delay)) }, - summary = { Text(stringResource(configuration.unifiedDelay.textRes)) }, - valueToText = { AnnotatedString(stringResource(it.textRes)) }, - ) - } - item(key = "geodataMode", contentType = "ListPreference") { - ListPreference( - value = configuration.geodataMode, - onValueChange = actions::updateGeodataMode, - values = booleanOptions, - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.geodata_mode)) }, - summary = { Text(stringResource(configuration.geodataMode.textRes)) }, - valueToText = { AnnotatedString(stringResource(it.textRes)) }, - ) - } - item(key = "tcpConcurrent", contentType = "ListPreference") { - ListPreference( - value = configuration.tcpConcurrent, - onValueChange = actions::updateTcpConcurrent, - values = booleanOptions, - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.tcp_concurrent)) }, - summary = { Text(stringResource(configuration.tcpConcurrent.textRes)) }, - valueToText = { AnnotatedString(stringResource(it.textRes)) }, - ) - } - item(key = "findProcessMode", contentType = "ListPreference") { - ListPreference( - value = configuration.findProcessMode, - onValueChange = actions::updateFindProcessMode, - values = ConfigurationOverride.FindProcessMode.entries, - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.find_process_mode)) }, - summary = { Text(stringResource(configuration.findProcessMode.textRes)) }, - valueToText = { AnnotatedString(stringResource(it.textRes)) }, - ) - } + listPreference( + key = "unifiedDelay", + value = configuration.unifiedDelay, + onValueChange = actions::updateUnifiedDelay, + values = booleanOptions, + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.unified_delay)) }, + summary = { Text(stringResource(configuration.unifiedDelay.textRes)) }, + valueToText = { AnnotatedString(stringResource(it.textRes)) }, + ) + listPreference( + key = "geodataMode", + value = configuration.geodataMode, + onValueChange = actions::updateGeodataMode, + values = booleanOptions, + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.geodata_mode)) }, + summary = { Text(stringResource(configuration.geodataMode.textRes)) }, + valueToText = { AnnotatedString(stringResource(it.textRes)) }, + ) + listPreference( + key = "tcpConcurrent", + value = configuration.tcpConcurrent, + onValueChange = actions::updateTcpConcurrent, + values = booleanOptions, + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.tcp_concurrent)) }, + summary = { Text(stringResource(configuration.tcpConcurrent.textRes)) }, + valueToText = { AnnotatedString(stringResource(it.textRes)) }, + ) + listPreference( + key = "findProcessMode", + value = configuration.findProcessMode, + onValueChange = actions::updateFindProcessMode, + values = ConfigurationOverride.FindProcessMode.entries, + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.find_process_mode)) }, + summary = { Text(stringResource(configuration.findProcessMode.textRes)) }, + valueToText = { AnnotatedString(stringResource(it.textRes)) }, + ) } private fun LazyListScope.metaSnifferPreferenceItems( @@ -302,214 +298,184 @@ private fun LazyListScope.metaSnifferPreferenceItems( key = "cat_sniffer", title = { Text(stringResource(R.string.sniffer_setting)) }, ) - - item(key = "snifferEnable", contentType = "ListPreference") { - ListPreference( - value = configuration.sniffer.enable, - onValueChange = actions::updateSnifferEnable, - values = booleanOptions, - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.strategy)) }, - summary = { Text(stringResource(configuration.sniffer.enable.textRes)) }, - valueToText = { AnnotatedString(stringResource(it.textRes)) }, - ) - } - item(key = "sniffHttpPorts", contentType = "EditTextListPreference") { - val enabled = configuration.sniffer.enable != false - Preference( - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.sniff_http_ports)) }, - summary = { Text(configuration.sniffer.sniff.http.ports.listSummary(R.string.dont_modify)) }, - enabled = enabled, - onClick = { - onOpenEditableTextList( - R.string.sniff_http_ports, - configuration.sniffer.sniff.http.ports, - actions::updateSniffHttpPorts, - ) - }, - ) - } - item(key = "sniffHttpOverrideDestination", contentType = "ListPreference") { - val enabled = configuration.sniffer.enable != false - ListPreference( - value = configuration.sniffer.sniff.http.overrideDestination, - onValueChange = actions::updateSniffHttpOverrideDestination, - values = booleanOptions, - modifier = Modifier.fillMaxWidth(), - enabled = enabled, - title = { Text(stringResource(R.string.sniff_http_override_destination)) }, - summary = { - Text(stringResource(configuration.sniffer.sniff.http.overrideDestination.textRes)) - }, - valueToText = { AnnotatedString(stringResource(it.textRes)) }, - ) - } - item(key = "sniffTlsPorts", contentType = "EditTextListPreference") { - val enabled = configuration.sniffer.enable != false - Preference( - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.sniff_tls_ports)) }, - summary = { Text(configuration.sniffer.sniff.tls.ports.listSummary(R.string.dont_modify)) }, - enabled = enabled, - onClick = { - onOpenEditableTextList( - R.string.sniff_tls_ports, - configuration.sniffer.sniff.tls.ports, - actions::updateSniffTlsPorts, - ) - }, - ) - } - item(key = "sniffTlsOverrideDestination", contentType = "ListPreference") { - val enabled = configuration.sniffer.enable != false - ListPreference( - value = configuration.sniffer.sniff.tls.overrideDestination, - onValueChange = actions::updateSniffTlsOverrideDestination, - values = booleanOptions, - modifier = Modifier.fillMaxWidth(), - enabled = enabled, - title = { Text(stringResource(R.string.sniff_tls_override_destination)) }, - summary = { - Text(stringResource(configuration.sniffer.sniff.tls.overrideDestination.textRes)) - }, - valueToText = { AnnotatedString(stringResource(it.textRes)) }, - ) - } - item(key = "sniffQuicPorts", contentType = "EditTextListPreference") { - val enabled = configuration.sniffer.enable != false - Preference( - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.sniff_quic_ports)) }, - summary = { Text(configuration.sniffer.sniff.quic.ports.listSummary(R.string.dont_modify)) }, - enabled = enabled, - onClick = { - onOpenEditableTextList( - R.string.sniff_quic_ports, - configuration.sniffer.sniff.quic.ports, - actions::updateSniffQuicPorts, - ) - }, - ) - } - item(key = "sniffQuicOverrideDestination", contentType = "ListPreference") { - val enabled = configuration.sniffer.enable != false - ListPreference( - value = configuration.sniffer.sniff.quic.overrideDestination, - onValueChange = actions::updateSniffQuicOverrideDestination, - values = booleanOptions, - modifier = Modifier.fillMaxWidth(), - enabled = enabled, - title = { Text(stringResource(R.string.sniff_quic_override_destination)) }, - summary = { - Text(stringResource(configuration.sniffer.sniff.quic.overrideDestination.textRes)) - }, - valueToText = { AnnotatedString(stringResource(it.textRes)) }, - ) - } - item(key = "forceDnsMapping", contentType = "ListPreference") { - val enabled = configuration.sniffer.enable != false - ListPreference( - value = configuration.sniffer.forceDnsMapping, - onValueChange = actions::updateForceDnsMapping, - values = booleanOptions, - modifier = Modifier.fillMaxWidth(), - enabled = enabled, - title = { Text(stringResource(R.string.force_dns_mapping)) }, - summary = { Text(stringResource(configuration.sniffer.forceDnsMapping.textRes)) }, - valueToText = { AnnotatedString(stringResource(it.textRes)) }, - ) - } - item(key = "parsePureIp", contentType = "ListPreference") { - val enabled = configuration.sniffer.enable != false - ListPreference( - value = configuration.sniffer.parsePureIp, - onValueChange = actions::updateParsePureIp, - values = booleanOptions, - modifier = Modifier.fillMaxWidth(), - enabled = enabled, - title = { Text(stringResource(R.string.parse_pure_ip)) }, - summary = { Text(stringResource(configuration.sniffer.parsePureIp.textRes)) }, - valueToText = { AnnotatedString(stringResource(it.textRes)) }, - ) - } - item(key = "overrideDestination", contentType = "ListPreference") { - val enabled = configuration.sniffer.enable != false - ListPreference( - value = configuration.sniffer.overrideDestination, - onValueChange = actions::updateOverrideDestination, - values = booleanOptions, - modifier = Modifier.fillMaxWidth(), - enabled = enabled, - title = { Text(stringResource(R.string.override_destination)) }, - summary = { Text(stringResource(configuration.sniffer.overrideDestination.textRes)) }, - valueToText = { AnnotatedString(stringResource(it.textRes)) }, - ) - } - item(key = "forceDomain", contentType = "EditTextListPreference") { - val enabled = configuration.sniffer.enable != false - Preference( - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.force_domain)) }, - summary = { Text(configuration.sniffer.forceDomain.listSummary(R.string.dont_modify)) }, - enabled = enabled, - onClick = { - onOpenEditableTextList( - R.string.force_domain, - configuration.sniffer.forceDomain, - actions::updateForceDomain, - ) - }, - ) - } - item(key = "skipDomain", contentType = "EditTextListPreference") { - val enabled = configuration.sniffer.enable != false - Preference( - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.skip_domain)) }, - summary = { Text(configuration.sniffer.skipDomain.listSummary(R.string.dont_modify)) }, - enabled = enabled, - onClick = { - onOpenEditableTextList( - R.string.skip_domain, - configuration.sniffer.skipDomain, - actions::updateSkipDomain, - ) - }, - ) - } - item(key = "skipSrcAddress", contentType = "EditTextListPreference") { - val enabled = configuration.sniffer.enable != false - Preference( - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.skip_src_address)) }, - summary = { Text(configuration.sniffer.skipSrcAddress.listSummary(R.string.dont_modify)) }, - enabled = enabled, - onClick = { - onOpenEditableTextList( - R.string.skip_src_address, - configuration.sniffer.skipSrcAddress, - actions::updateSkipSrcAddress, - ) - }, - ) - } - item(key = "skipDstAddress", contentType = "EditTextListPreference") { - val enabled = configuration.sniffer.enable != false - Preference( - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.skip_dst_address)) }, - summary = { Text(configuration.sniffer.skipDstAddress.listSummary(R.string.dont_modify)) }, - enabled = enabled, - onClick = { - onOpenEditableTextList( - R.string.skip_dst_address, - configuration.sniffer.skipDstAddress, - actions::updateSkipDstAddress, - ) - }, - ) - } + listPreference( + key = "snifferEnable", + value = configuration.sniffer.enable, + onValueChange = actions::updateSnifferEnable, + values = booleanOptions, + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.strategy)) }, + summary = { Text(stringResource(configuration.sniffer.enable.textRes)) }, + valueToText = { AnnotatedString(stringResource(it.textRes)) }, + ) + preference( + key = "sniffHttpPorts", + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.sniff_http_ports)) }, + summary = { Text(configuration.sniffer.sniff.http.ports.listSummary(R.string.dont_modify)) }, + enabled = configuration.sniffer.enable != false, + onClick = { + onOpenEditableTextList( + R.string.sniff_http_ports, + configuration.sniffer.sniff.http.ports, + actions::updateSniffHttpPorts, + ) + }, + ) + listPreference( + key = "sniffHttpOverrideDestination", + value = configuration.sniffer.sniff.http.overrideDestination, + onValueChange = actions::updateSniffHttpOverrideDestination, + values = booleanOptions, + modifier = Modifier.fillMaxWidth(), + enabled = configuration.sniffer.enable != false, + title = { Text(stringResource(R.string.sniff_http_override_destination)) }, + summary = { + Text(stringResource(configuration.sniffer.sniff.http.overrideDestination.textRes)) + }, + valueToText = { AnnotatedString(stringResource(it.textRes)) }, + ) + preference( + key = "sniffTlsPorts", + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.sniff_tls_ports)) }, + summary = { Text(configuration.sniffer.sniff.tls.ports.listSummary(R.string.dont_modify)) }, + enabled = configuration.sniffer.enable != false, + onClick = { + onOpenEditableTextList( + R.string.sniff_tls_ports, + configuration.sniffer.sniff.tls.ports, + actions::updateSniffTlsPorts, + ) + }, + ) + listPreference( + key = "sniffTlsOverrideDestination", + value = configuration.sniffer.sniff.tls.overrideDestination, + onValueChange = actions::updateSniffTlsOverrideDestination, + values = booleanOptions, + modifier = Modifier.fillMaxWidth(), + enabled = configuration.sniffer.enable != false, + title = { Text(stringResource(R.string.sniff_tls_override_destination)) }, + summary = { Text(stringResource(configuration.sniffer.sniff.tls.overrideDestination.textRes)) }, + valueToText = { AnnotatedString(stringResource(it.textRes)) }, + ) + preference( + key = "sniffQuicPorts", + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.sniff_quic_ports)) }, + summary = { Text(configuration.sniffer.sniff.quic.ports.listSummary(R.string.dont_modify)) }, + enabled = configuration.sniffer.enable != false, + onClick = { + onOpenEditableTextList( + R.string.sniff_quic_ports, + configuration.sniffer.sniff.quic.ports, + actions::updateSniffQuicPorts, + ) + }, + ) + listPreference( + key = "sniffQuicOverrideDestination", + value = configuration.sniffer.sniff.quic.overrideDestination, + onValueChange = actions::updateSniffQuicOverrideDestination, + values = booleanOptions, + modifier = Modifier.fillMaxWidth(), + enabled = configuration.sniffer.enable != false, + title = { Text(stringResource(R.string.sniff_quic_override_destination)) }, + summary = { + Text(stringResource(configuration.sniffer.sniff.quic.overrideDestination.textRes)) + }, + valueToText = { AnnotatedString(stringResource(it.textRes)) }, + ) + listPreference( + key = "forceDnsMapping", + value = configuration.sniffer.forceDnsMapping, + onValueChange = actions::updateForceDnsMapping, + values = booleanOptions, + modifier = Modifier.fillMaxWidth(), + enabled = configuration.sniffer.enable != false, + title = { Text(stringResource(R.string.force_dns_mapping)) }, + summary = { Text(stringResource(configuration.sniffer.forceDnsMapping.textRes)) }, + valueToText = { AnnotatedString(stringResource(it.textRes)) }, + ) + listPreference( + key = "parsePureIp", + value = configuration.sniffer.parsePureIp, + onValueChange = actions::updateParsePureIp, + values = booleanOptions, + modifier = Modifier.fillMaxWidth(), + enabled = configuration.sniffer.enable != false, + title = { Text(stringResource(R.string.parse_pure_ip)) }, + summary = { Text(stringResource(configuration.sniffer.parsePureIp.textRes)) }, + valueToText = { AnnotatedString(stringResource(it.textRes)) }, + ) + listPreference( + key = "overrideDestination", + value = configuration.sniffer.overrideDestination, + onValueChange = actions::updateOverrideDestination, + values = booleanOptions, + modifier = Modifier.fillMaxWidth(), + enabled = configuration.sniffer.enable != false, + title = { Text(stringResource(R.string.override_destination)) }, + summary = { Text(stringResource(configuration.sniffer.overrideDestination.textRes)) }, + valueToText = { AnnotatedString(stringResource(it.textRes)) }, + ) + preference( + key = "forceDomain", + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.force_domain)) }, + summary = { Text(configuration.sniffer.forceDomain.listSummary(R.string.dont_modify)) }, + enabled = configuration.sniffer.enable != false, + onClick = { + onOpenEditableTextList( + R.string.force_domain, + configuration.sniffer.forceDomain, + actions::updateForceDomain, + ) + }, + ) + preference( + key = "skipDomain", + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.skip_domain)) }, + summary = { Text(configuration.sniffer.skipDomain.listSummary(R.string.dont_modify)) }, + enabled = configuration.sniffer.enable != false, + onClick = { + onOpenEditableTextList( + R.string.skip_domain, + configuration.sniffer.skipDomain, + actions::updateSkipDomain, + ) + }, + ) + preference( + key = "skipSrcAddress", + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.skip_src_address)) }, + summary = { Text(configuration.sniffer.skipSrcAddress.listSummary(R.string.dont_modify)) }, + enabled = configuration.sniffer.enable != false, + onClick = { + onOpenEditableTextList( + R.string.skip_src_address, + configuration.sniffer.skipSrcAddress, + actions::updateSkipSrcAddress, + ) + }, + ) + preference( + key = "skipDstAddress", + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.skip_dst_address)) }, + summary = { Text(configuration.sniffer.skipDstAddress.listSummary(R.string.dont_modify)) }, + enabled = configuration.sniffer.enable != false, + onClick = { + onOpenEditableTextList( + R.string.skip_dst_address, + configuration.sniffer.skipDstAddress, + actions::updateSkipDstAddress, + ) + }, + ) } private fun LazyListScope.metaGeoFileItems( @@ -520,38 +486,34 @@ private fun LazyListScope.metaGeoFileItems( ) { preferenceCategory(key = "cat_geox", title = { Text(stringResource(R.string.geox_files)) }) - item(key = "importGeoIp", contentType = "ClickablePreference") { - Preference( - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.import_geoip_file)) }, - summary = { Text(stringResource(R.string.press_to_import)) }, - onClick = onImportGeoIp, - ) - } - item(key = "importGeoSite", contentType = "ClickablePreference") { - Preference( - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.import_geosite_file)) }, - summary = { Text(stringResource(R.string.press_to_import)) }, - onClick = onImportGeoSite, - ) - } - item(key = "importCountry", contentType = "ClickablePreference") { - Preference( - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.import_country_file)) }, - summary = { Text(stringResource(R.string.press_to_import)) }, - onClick = onImportCountry, - ) - } - item(key = "importASN", contentType = "ClickablePreference") { - Preference( - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.import_asn_file)) }, - summary = { Text(stringResource(R.string.press_to_import)) }, - onClick = onImportASN, - ) - } + preference( + key = "importGeoIp", + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.import_geoip_file)) }, + summary = { Text(stringResource(R.string.press_to_import)) }, + onClick = onImportGeoIp, + ) + preference( + key = "importGeoSite", + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.import_geosite_file)) }, + summary = { Text(stringResource(R.string.press_to_import)) }, + onClick = onImportGeoSite, + ) + preference( + key = "importCountry", + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.import_country_file)) }, + summary = { Text(stringResource(R.string.press_to_import)) }, + onClick = onImportCountry, + ) + preference( + key = "importASN", + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.import_asn_file)) }, + summary = { Text(stringResource(R.string.press_to_import)) }, + onClick = onImportASN, + ) } private val ConfigurationOverride.FindProcessMode?.textRes: Int diff --git a/app/src/main/kotlin/com/github/kr328/clash/settings/ui/NetworkSettingsScreen.kt b/app/src/main/kotlin/com/github/kr328/clash/settings/ui/NetworkSettingsScreen.kt index a47e97f9d6..0c375d1005 100644 --- a/app/src/main/kotlin/com/github/kr328/clash/settings/ui/NetworkSettingsScreen.kt +++ b/app/src/main/kotlin/com/github/kr328/clash/settings/ui/NetworkSettingsScreen.kt @@ -25,11 +25,11 @@ import com.github.kr328.clash.ui.icon.BaselineVpnLock import com.github.kr328.clash.ui.icon.MihomoIcons import com.github.kr328.clash.ui.theme.MihomoTheme import com.github.kr328.clash.ui.theme.PreviewMihomo -import me.zhanghai.compose.preference.ListPreference -import me.zhanghai.compose.preference.Preference import me.zhanghai.compose.preference.ProvidePreferenceLocals -import me.zhanghai.compose.preference.SwitchPreference +import me.zhanghai.compose.preference.listPreference +import me.zhanghai.compose.preference.preference import me.zhanghai.compose.preference.preferenceCategory +import me.zhanghai.compose.preference.switchPreference @Composable fun NetworkSettingsScreen( @@ -95,108 +95,95 @@ private fun NetworkSettingsContent( ) { innerPadding -> ProvidePreferenceLocals { LazyColumn(modifier = Modifier.fillMaxSize(), contentPadding = innerPadding) { - item(key = "route_system_traffic") { - SwitchPreference( - value = uiState.enableVpn, - onValueChange = onEnableVpnChange, - enabled = !clashRunning, - icon = { Icon(imageVector = MihomoIcons.BaselineVpnLock, contentDescription = null) }, - title = { Text(stringResource(R.string.route_system_traffic)) }, - summary = { Text(stringResource(R.string.routing_via_vpn_service)) }, - ) - } + switchPreference( + key = "route_system_traffic", + value = uiState.enableVpn, + onValueChange = onEnableVpnChange, + enabled = !clashRunning, + icon = { Icon(imageVector = MihomoIcons.BaselineVpnLock, contentDescription = null) }, + title = { Text(stringResource(R.string.route_system_traffic)) }, + summary = { Text(stringResource(R.string.routing_via_vpn_service)) }, + ) preferenceCategory( key = "cat_vpn_service_options", title = { Text(stringResource(R.string.vpn_service_options)) }, ) - item(key = "bypass_private_network") { - SwitchPreference( - value = uiState.bypassPrivateNetwork, - onValueChange = onBypassPrivateNetworkChange, - enabled = vpnDependenciesEnabled, - title = { Text(stringResource(R.string.bypass_private_network)) }, - summary = { Text(stringResource(R.string.bypass_private_network_summary)) }, - ) - } - item(key = "dns_hijacking") { - SwitchPreference( - value = uiState.dnsHijacking, - onValueChange = onDnsHijackingChange, - enabled = vpnDependenciesEnabled, - title = { Text(stringResource(R.string.dns_hijacking)) }, - summary = { Text(stringResource(R.string.dns_hijacking_summary)) }, - ) - } - item(key = "allow_bypass") { - SwitchPreference( - value = uiState.allowBypass, - onValueChange = onAllowBypassChange, - enabled = vpnDependenciesEnabled, - title = { Text(stringResource(R.string.allow_bypass)) }, - summary = { Text(stringResource(R.string.allow_bypass_summary)) }, - ) - } - item(key = "allow_ipv6") { - SwitchPreference( - value = uiState.allowIpv6, - onValueChange = onAllowIpv6Change, - enabled = vpnDependenciesEnabled, - title = { Text(stringResource(R.string.allow_ipv6)) }, - summary = { Text(stringResource(R.string.allow_ipv6_summary)) }, - ) - } + switchPreference( + key = "bypass_private_network", + value = uiState.bypassPrivateNetwork, + onValueChange = onBypassPrivateNetworkChange, + enabled = vpnDependenciesEnabled, + title = { Text(stringResource(R.string.bypass_private_network)) }, + summary = { Text(stringResource(R.string.bypass_private_network_summary)) }, + ) + switchPreference( + key = "dns_hijacking", + value = uiState.dnsHijacking, + onValueChange = onDnsHijackingChange, + enabled = vpnDependenciesEnabled, + title = { Text(stringResource(R.string.dns_hijacking)) }, + summary = { Text(stringResource(R.string.dns_hijacking_summary)) }, + ) + switchPreference( + key = "allow_bypass", + value = uiState.allowBypass, + onValueChange = onAllowBypassChange, + enabled = vpnDependenciesEnabled, + title = { Text(stringResource(R.string.allow_bypass)) }, + summary = { Text(stringResource(R.string.allow_bypass_summary)) }, + ) + switchPreference( + key = "allow_ipv6", + value = uiState.allowIpv6, + onValueChange = onAllowIpv6Change, + enabled = vpnDependenciesEnabled, + title = { Text(stringResource(R.string.allow_ipv6)) }, + summary = { Text(stringResource(R.string.allow_ipv6_summary)) }, + ) if (uiState.hasSystemProxyOption) { - item(key = "system_proxy") { - SwitchPreference( - value = uiState.systemProxy, - onValueChange = onSystemProxyChange, - enabled = vpnDependenciesEnabled, - title = { Text(stringResource(R.string.system_proxy)) }, - summary = { Text(stringResource(R.string.system_proxy_summary)) }, - ) - } - } - item(key = "tun_stack_mode") { - ListPreference( - value = tunStackMode, - onValueChange = { onTunStackModeChange(it.persistedValue) }, - values = TunStackMode.entries, - enabled = vpnDependenciesEnabled, - title = { Text(stringResource(R.string.tun_stack_mode)) }, - summary = { Text(stringResource(tunStackMode.summaryRes)) }, - valueToText = { - androidx.compose.ui.text.AnnotatedString(stringResource(it.summaryRes)) - }, - ) - } - item(key = "access_control_mode") { - ListPreference( - value = uiState.accessControlMode, - onValueChange = onAccessControlModeChange, - values = - listOf( - AccessControlMode.AcceptAll, - AccessControlMode.AcceptSelected, - AccessControlMode.DenySelected, - ), + switchPreference( + key = "system_proxy", + value = uiState.systemProxy, + onValueChange = onSystemProxyChange, enabled = vpnDependenciesEnabled, - title = { Text(stringResource(R.string.access_control_mode)) }, - summary = { Text(stringResource(uiState.accessControlMode.summaryRes)) }, - valueToText = { - androidx.compose.ui.text.AnnotatedString(stringResource(it.summaryRes)) - }, - ) - } - item(key = "access_control_packages") { - Preference( - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.access_control_packages)) }, - summary = { Text(stringResource(R.string.access_control_packages_summary)) }, - onClick = onAccessControlPackagesClick, + title = { Text(stringResource(R.string.system_proxy)) }, + summary = { Text(stringResource(R.string.system_proxy_summary)) }, ) } + listPreference( + key = "tun_stack_mode", + value = tunStackMode, + onValueChange = { onTunStackModeChange(it.persistedValue) }, + values = TunStackMode.entries, + enabled = vpnDependenciesEnabled, + title = { Text(stringResource(R.string.tun_stack_mode)) }, + summary = { Text(stringResource(tunStackMode.summaryRes)) }, + valueToText = { androidx.compose.ui.text.AnnotatedString(stringResource(it.summaryRes)) }, + ) + listPreference( + key = "access_control_mode", + value = uiState.accessControlMode, + onValueChange = onAccessControlModeChange, + values = + listOf( + AccessControlMode.AcceptAll, + AccessControlMode.AcceptSelected, + AccessControlMode.DenySelected, + ), + enabled = vpnDependenciesEnabled, + title = { Text(stringResource(R.string.access_control_mode)) }, + summary = { Text(stringResource(uiState.accessControlMode.summaryRes)) }, + valueToText = { androidx.compose.ui.text.AnnotatedString(stringResource(it.summaryRes)) }, + ) + preference( + key = "access_control_packages", + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.access_control_packages)) }, + summary = { Text(stringResource(R.string.access_control_packages_summary)) }, + onClick = onAccessControlPackagesClick, + ) } } } diff --git a/app/src/main/kotlin/com/github/kr328/clash/settings/ui/OverrideSettingsScreen.kt b/app/src/main/kotlin/com/github/kr328/clash/settings/ui/OverrideSettingsScreen.kt index 1d742685d6..c2db7601c9 100644 --- a/app/src/main/kotlin/com/github/kr328/clash/settings/ui/OverrideSettingsScreen.kt +++ b/app/src/main/kotlin/com/github/kr328/clash/settings/ui/OverrideSettingsScreen.kt @@ -50,9 +50,10 @@ import com.github.kr328.clash.ui.icon.MihomoIcons import com.github.kr328.clash.ui.theme.MihomoTheme import com.github.kr328.clash.ui.theme.PreviewMihomo import kotlinx.serialization.Serializable -import me.zhanghai.compose.preference.ListPreference import me.zhanghai.compose.preference.Preference import me.zhanghai.compose.preference.ProvidePreferenceLocals +import me.zhanghai.compose.preference.listPreference +import me.zhanghai.compose.preference.preference import me.zhanghai.compose.preference.preferenceCategory private sealed interface OverrideSettingsRoute : NavKey { @@ -208,187 +209,170 @@ private fun LazyListScope.generalPreferenceItems( onOpenEditableTextList: (Int, List?, (List?) -> Unit) -> Unit, ) { preferenceCategory(key = "cat_general", title = { Text(stringResource(R.string.general)) }) - item(key = "httpPort", contentType = "EditTextPreference") { - OverrideEditTextPreferenceItem( - title = R.string.http_port, - placeholder = R.string.dont_modify, - emptyLabel = R.string.disabled, - value = portText(configuration.httpPort), - onValueChange = { actions.updateHttpPort(parsePort(it)) }, - numericOnly = true, - ) - } - item(key = "socksPort", contentType = "EditTextPreference") { - OverrideEditTextPreferenceItem( - title = R.string.socks_port, - placeholder = R.string.dont_modify, - emptyLabel = R.string.disabled, - value = portText(configuration.socksPort), - onValueChange = { actions.updateSocksPort(parsePort(it)) }, - numericOnly = true, - ) - } - item(key = "redirectPort", contentType = "EditTextPreference") { - OverrideEditTextPreferenceItem( - title = R.string.redirect_port, - placeholder = R.string.dont_modify, - emptyLabel = R.string.disabled, - value = portText(configuration.redirectPort), - onValueChange = { actions.updateRedirectPort(parsePort(it)) }, - numericOnly = true, - ) - } - item(key = "tproxyPort", contentType = "EditTextPreference") { - OverrideEditTextPreferenceItem( - title = R.string.tproxy_port, - placeholder = R.string.dont_modify, - emptyLabel = R.string.disabled, - value = portText(configuration.tproxyPort), - onValueChange = { actions.updateTproxyPort(parsePort(it)) }, - numericOnly = true, - ) - } - item(key = "mixedPort", contentType = "EditTextPreference") { - OverrideEditTextPreferenceItem( - title = R.string.mixed_port, - placeholder = R.string.dont_modify, - emptyLabel = R.string.disabled, - value = portText(configuration.mixedPort), - onValueChange = { actions.updateMixedPort(parsePort(it)) }, - numericOnly = true, - ) - } - item(key = "authentication", contentType = "EditTextListPreference") { - Preference( - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.authentication)) }, - summary = { Text(configuration.authentication.listSummary(R.string.dont_modify)) }, - onClick = { - onOpenEditableTextList( - R.string.authentication, - configuration.authentication, - actions::updateAuthentication, - ) - }, - ) - } - item(key = "allowLan", contentType = "ListPreference") { - ListPreference( - value = configuration.allowLan, - onValueChange = actions::updateAllowLan, - values = booleanOptions, - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.allow_lan)) }, - summary = { Text(stringResource(configuration.allowLan.textRes)) }, - valueToText = { AnnotatedString(stringResource(it.textRes)) }, - ) - } - item(key = "ipv6", contentType = "ListPreference") { - ListPreference( - value = configuration.ipv6, - onValueChange = actions::updateIpv6, - values = booleanOptions, - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.ipv6)) }, - summary = { Text(stringResource(configuration.ipv6.textRes)) }, - valueToText = { AnnotatedString(stringResource(it.textRes)) }, - ) - } - item(key = "bindAddress", contentType = "EditTextPreference") { - OverrideEditTextPreferenceItem( - title = R.string.bind_address, - placeholder = R.string.dont_modify, - emptyLabel = R.string.default_, - value = configuration.bindAddress, - onValueChange = actions::updateBindAddress, - ) - } - item(key = "externalController", contentType = "EditTextPreference") { - OverrideEditTextPreferenceItem( - title = R.string.external_controller, - placeholder = R.string.dont_modify, - emptyLabel = R.string.default_, - value = configuration.externalController, - onValueChange = actions::updateExternalController, - ) - } - item(key = "externalControllerTls", contentType = "EditTextPreference") { - OverrideEditTextPreferenceItem( - title = R.string.external_controller_tls, - placeholder = R.string.dont_modify, - emptyLabel = R.string.default_, - value = configuration.externalControllerTLS, - onValueChange = actions::updateExternalControllerTls, - ) - } - item(key = "allowOrigins", contentType = "EditTextListPreference") { - Preference( - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.allow_origins)) }, - summary = { - Text(configuration.externalControllerCors.allowOrigins.listSummary(R.string.dont_modify)) - }, - onClick = { - onOpenEditableTextList( - R.string.allow_origins, - configuration.externalControllerCors.allowOrigins, - actions::updateAllowOrigins, - ) - }, - ) - } - item(key = "allowPrivateNetwork", contentType = "ListPreference") { - ListPreference( - value = configuration.externalControllerCors.allowPrivateNetwork, - onValueChange = actions::updateAllowPrivateNetwork, - values = booleanOptions, - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.allow_private_network)) }, - summary = { - Text(stringResource(configuration.externalControllerCors.allowPrivateNetwork.textRes)) - }, - valueToText = { AnnotatedString(stringResource(it.textRes)) }, - ) - } - item(key = "secret", contentType = "EditTextPreference") { - OverrideEditTextPreferenceItem( - title = R.string.secret, - placeholder = R.string.dont_modify, - emptyLabel = R.string.default_, - value = configuration.secret, - onValueChange = actions::updateSecret, - ) - } - item(key = "mode", contentType = "ListPreference") { - ListPreference( - value = configuration.mode, - onValueChange = actions::updateMode, - values = TunnelState.Mode.entries, - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.mode)) }, - summary = { Text(stringResource(configuration.mode.textRes)) }, - valueToText = { AnnotatedString(stringResource(it.textRes)) }, - ) - } - item(key = "logLevel", contentType = "ListPreference") { - ListPreference( - value = configuration.logLevel, - onValueChange = actions::updateLogLevel, - values = LogMessage.Level.entries, - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.log_level)) }, - summary = { Text(stringResource(configuration.logLevel.textRes)) }, - valueToText = { AnnotatedString(stringResource(it.textRes)) }, - ) - } - item(key = "hosts", contentType = "EditTextMapPreference") { - Preference( - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.hosts)) }, - summary = { Text(configuration.hosts.summary(R.string.dont_modify)) }, - onClick = { onOpenEditableTextMap(R.string.hosts, configuration.hosts, actions::updateHosts) }, - ) - } + overrideEditTextPreferenceItem( + key = "httpPort", + title = R.string.http_port, + placeholder = R.string.dont_modify, + emptyLabel = R.string.disabled, + value = portText(configuration.httpPort), + onValueChange = { actions.updateHttpPort(parsePort(it)) }, + numericOnly = true, + ) + overrideEditTextPreferenceItem( + key = "socksPort", + title = R.string.socks_port, + placeholder = R.string.dont_modify, + emptyLabel = R.string.disabled, + value = portText(configuration.socksPort), + onValueChange = { actions.updateSocksPort(parsePort(it)) }, + numericOnly = true, + ) + overrideEditTextPreferenceItem( + key = "redirectPort", + title = R.string.redirect_port, + placeholder = R.string.dont_modify, + emptyLabel = R.string.disabled, + value = portText(configuration.redirectPort), + onValueChange = { actions.updateRedirectPort(parsePort(it)) }, + numericOnly = true, + ) + overrideEditTextPreferenceItem( + key = "tproxyPort", + title = R.string.tproxy_port, + placeholder = R.string.dont_modify, + emptyLabel = R.string.disabled, + value = portText(configuration.tproxyPort), + onValueChange = { actions.updateTproxyPort(parsePort(it)) }, + numericOnly = true, + ) + overrideEditTextPreferenceItem( + key = "mixedPort", + title = R.string.mixed_port, + placeholder = R.string.dont_modify, + emptyLabel = R.string.disabled, + value = portText(configuration.mixedPort), + onValueChange = { actions.updateMixedPort(parsePort(it)) }, + numericOnly = true, + ) + preference( + key = "authentication", + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.authentication)) }, + summary = { Text(configuration.authentication.listSummary(R.string.dont_modify)) }, + onClick = { + onOpenEditableTextList( + R.string.authentication, + configuration.authentication, + actions::updateAuthentication, + ) + }, + ) + listPreference( + key = "allowLan", + value = configuration.allowLan, + onValueChange = actions::updateAllowLan, + values = booleanOptions, + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.allow_lan)) }, + summary = { Text(stringResource(configuration.allowLan.textRes)) }, + valueToText = { AnnotatedString(stringResource(it.textRes)) }, + ) + listPreference( + key = "ipv6", + value = configuration.ipv6, + onValueChange = actions::updateIpv6, + values = booleanOptions, + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.ipv6)) }, + summary = { Text(stringResource(configuration.ipv6.textRes)) }, + valueToText = { AnnotatedString(stringResource(it.textRes)) }, + ) + overrideEditTextPreferenceItem( + key = "bindAddress", + title = R.string.bind_address, + placeholder = R.string.dont_modify, + emptyLabel = R.string.default_, + value = configuration.bindAddress, + onValueChange = actions::updateBindAddress, + ) + overrideEditTextPreferenceItem( + key = "externalController", + title = R.string.external_controller, + placeholder = R.string.dont_modify, + emptyLabel = R.string.default_, + value = configuration.externalController, + onValueChange = actions::updateExternalController, + ) + overrideEditTextPreferenceItem( + key = "externalControllerTls", + title = R.string.external_controller_tls, + placeholder = R.string.dont_modify, + emptyLabel = R.string.default_, + value = configuration.externalControllerTLS, + onValueChange = actions::updateExternalControllerTls, + ) + preference( + key = "allowOrigins", + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.allow_origins)) }, + summary = { + Text(configuration.externalControllerCors.allowOrigins.listSummary(R.string.dont_modify)) + }, + onClick = { + onOpenEditableTextList( + R.string.allow_origins, + configuration.externalControllerCors.allowOrigins, + actions::updateAllowOrigins, + ) + }, + ) + listPreference( + key = "allowPrivateNetwork", + value = configuration.externalControllerCors.allowPrivateNetwork, + onValueChange = actions::updateAllowPrivateNetwork, + values = booleanOptions, + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.allow_private_network)) }, + summary = { + Text(stringResource(configuration.externalControllerCors.allowPrivateNetwork.textRes)) + }, + valueToText = { AnnotatedString(stringResource(it.textRes)) }, + ) + overrideEditTextPreferenceItem( + key = "secret", + title = R.string.secret, + placeholder = R.string.dont_modify, + emptyLabel = R.string.default_, + value = configuration.secret, + onValueChange = actions::updateSecret, + ) + listPreference( + key = "mode", + value = configuration.mode, + onValueChange = actions::updateMode, + values = TunnelState.Mode.entries, + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.mode)) }, + summary = { Text(stringResource(configuration.mode.textRes)) }, + valueToText = { AnnotatedString(stringResource(it.textRes)) }, + ) + listPreference( + key = "logLevel", + value = configuration.logLevel, + onValueChange = actions::updateLogLevel, + values = LogMessage.Level.entries, + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.log_level)) }, + summary = { Text(stringResource(configuration.logLevel.textRes)) }, + valueToText = { AnnotatedString(stringResource(it.textRes)) }, + ) + preference( + key = "hosts", + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.hosts)) }, + summary = { Text(configuration.hosts.summary(R.string.dont_modify)) }, + onClick = { onOpenEditableTextMap(R.string.hosts, configuration.hosts, actions::updateHosts) }, + ) } private fun LazyListScope.dnsPreferenceItems( @@ -399,230 +383,213 @@ private fun LazyListScope.dnsPreferenceItems( onOpenEditableTextList: (Int, List?, (List?) -> Unit) -> Unit, ) { preferenceCategory(key = "cat_dns", title = { Text(stringResource(R.string.dns)) }) - item(key = "dnsStrategy", contentType = "ListPreference") { - ListPreference( - value = dnsEnabled, - onValueChange = actions::updateDnsEnable, - values = booleanOptions, - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.strategy)) }, - summary = { Text(stringResource(dnsEnabled.dnsStrategyTextRes)) }, - valueToText = { AnnotatedString(stringResource(it.dnsStrategyTextRes)) }, - ) - } - item(key = "dnsPreferH3", contentType = "ListPreference") { - ListPreference( - value = configuration.dns.preferH3, - onValueChange = actions::updateDnsPreferH3, - values = booleanOptions, - modifier = Modifier.fillMaxWidth(), - enabled = dnsEnabled != false, - title = { Text(stringResource(R.string.prefer_h3)) }, - summary = { Text(stringResource(configuration.dns.preferH3.textRes)) }, - valueToText = { AnnotatedString(stringResource(it.textRes)) }, - ) - } - item(key = "dnsListen", contentType = "EditTextPreference") { - OverrideEditTextPreferenceItem( - title = R.string.listen, - placeholder = R.string.dont_modify, - emptyLabel = R.string.disabled, - value = configuration.dns.listen, - onValueChange = actions::updateDnsListen, - enabled = dnsEnabled != false, - ) - } - item(key = "appendSystemDns", contentType = "ListPreference") { - ListPreference( - value = configuration.app.appendSystemDns, - onValueChange = actions::updateAppendSystemDns, - values = booleanOptions, - modifier = Modifier.fillMaxWidth(), - enabled = dnsEnabled != false, - title = { Text(stringResource(R.string.append_system_dns)) }, - summary = { Text(stringResource(configuration.app.appendSystemDns.textRes)) }, - valueToText = { AnnotatedString(stringResource(it.textRes)) }, - ) - } - item(key = "dnsIpv6", contentType = "ListPreference") { - ListPreference( - value = configuration.dns.ipv6, - onValueChange = actions::updateDnsIpv6, - values = booleanOptions, - modifier = Modifier.fillMaxWidth(), - enabled = dnsEnabled != false, - title = { Text(stringResource(R.string.ipv6)) }, - summary = { Text(stringResource(configuration.dns.ipv6.textRes)) }, - valueToText = { AnnotatedString(stringResource(it.textRes)) }, - ) - } - item(key = "dnsUseHosts", contentType = "ListPreference") { - ListPreference( - value = configuration.dns.useHosts, - onValueChange = actions::updateDnsUseHosts, - values = booleanOptions, - modifier = Modifier.fillMaxWidth(), - enabled = dnsEnabled != false, - title = { Text(stringResource(R.string.use_hosts)) }, - summary = { Text(stringResource(configuration.dns.useHosts.textRes)) }, - valueToText = { AnnotatedString(stringResource(it.textRes)) }, - ) - } - item(key = "dnsEnhancedMode", contentType = "ListPreference") { - ListPreference( - value = configuration.dns.enhancedMode, - onValueChange = actions::updateDnsEnhancedMode, - values = ConfigurationOverride.DnsEnhancedMode.entries, - modifier = Modifier.fillMaxWidth(), - enabled = dnsEnabled != false, - title = { Text(stringResource(R.string.enhanced_mode)) }, - summary = { Text(stringResource(configuration.dns.enhancedMode.textRes)) }, - valueToText = { AnnotatedString(stringResource(it.textRes)) }, - ) - } - item(key = "dnsNameServer", contentType = "EditTextListPreference") { - Preference( - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.name_server)) }, - summary = { Text(configuration.dns.nameServer.listSummary(R.string.dont_modify)) }, - enabled = dnsEnabled != false, - onClick = { - onOpenEditableTextList( - R.string.name_server, - configuration.dns.nameServer, - actions::updateDnsNameServer, - ) - }, - ) - } - item(key = "dnsFallback", contentType = "EditTextListPreference") { - Preference( - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.fallback)) }, - summary = { Text(configuration.dns.fallback.listSummary(R.string.dont_modify)) }, - enabled = dnsEnabled != false, - onClick = { - onOpenEditableTextList( - R.string.fallback, - configuration.dns.fallback, - actions::updateDnsFallback, - ) - }, - ) - } - item(key = "dnsDefaultServer", contentType = "EditTextListPreference") { - Preference( - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.default_name_server)) }, - summary = { Text(configuration.dns.defaultServer.listSummary(R.string.dont_modify)) }, - enabled = dnsEnabled != false, - onClick = { - onOpenEditableTextList( - R.string.default_name_server, - configuration.dns.defaultServer, - actions::updateDnsDefaultServer, - ) - }, - ) - } - item(key = "dnsFakeIpFilter", contentType = "EditTextListPreference") { - Preference( - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.fakeip_filter)) }, - summary = { Text(configuration.dns.fakeIpFilter.listSummary(R.string.dont_modify)) }, - enabled = dnsEnabled != false, - onClick = { - onOpenEditableTextList( - R.string.fakeip_filter, - configuration.dns.fakeIpFilter, - actions::updateDnsFakeIpFilter, - ) - }, - ) - } - item(key = "dnsFakeIpFilterMode", contentType = "ListPreference") { - ListPreference( - value = configuration.dns.fakeIPFilterMode, - onValueChange = actions::updateDnsFakeIpFilterMode, - values = ConfigurationOverride.FilterMode.entries, - modifier = Modifier.fillMaxWidth(), - enabled = dnsEnabled != false, - title = { Text(stringResource(R.string.fakeip_filter_mode)) }, - summary = { Text(stringResource(configuration.dns.fakeIPFilterMode.textRes)) }, - valueToText = { AnnotatedString(stringResource(it.textRes)) }, - ) - } - item(key = "dnsGeoIpFallback", contentType = "ListPreference") { - ListPreference( - value = configuration.dns.fallbackFilter.geoIp, - onValueChange = actions::updateDnsGeoIpFallback, - values = booleanOptions, - modifier = Modifier.fillMaxWidth(), - enabled = dnsEnabled != false, - title = { Text(stringResource(R.string.geoip_fallback)) }, - summary = { Text(stringResource(configuration.dns.fallbackFilter.geoIp.textRes)) }, - valueToText = { AnnotatedString(stringResource(it.textRes)) }, - ) - } - item(key = "dnsGeoIpCode", contentType = "EditTextPreference") { - OverrideEditTextPreferenceItem( - title = R.string.geoip_fallback_code, - placeholder = R.string.dont_modify, - emptyLabel = R.string.raw_cn, - value = configuration.dns.fallbackFilter.geoIpCode, - onValueChange = actions::updateDnsGeoIpCode, - enabled = dnsEnabled != false, - ) - } - item(key = "dnsDomainFallback", contentType = "EditTextListPreference") { - Preference( - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.domain_fallback)) }, - summary = { Text(configuration.dns.fallbackFilter.domain.listSummary(R.string.dont_modify)) }, - enabled = dnsEnabled != false, - onClick = { - onOpenEditableTextList( - R.string.domain_fallback, - configuration.dns.fallbackFilter.domain, - actions::updateDnsDomainFallback, - ) - }, - ) - } - item(key = "dnsIpcidrFallback", contentType = "EditTextListPreference") { - Preference( - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.ipcidr_fallback)) }, - summary = { Text(configuration.dns.fallbackFilter.ipcidr.listSummary(R.string.dont_modify)) }, - enabled = dnsEnabled != false, - onClick = { - onOpenEditableTextList( - R.string.ipcidr_fallback, - configuration.dns.fallbackFilter.ipcidr, - actions::updateDnsIpcidrFallback, - ) - }, - ) - } - item(key = "dnsNameserverPolicy", contentType = "EditTextMapPreference") { - Preference( - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(R.string.name_server_policy)) }, - summary = { Text(configuration.dns.nameserverPolicy.summary(R.string.dont_modify)) }, - enabled = dnsEnabled != false, - onClick = { - onOpenEditableTextMap( - R.string.name_server_policy, - configuration.dns.nameserverPolicy, - actions::updateDnsNameserverPolicy, - ) - }, - ) - } + listPreference( + key = "dnsStrategy", + value = dnsEnabled, + onValueChange = actions::updateDnsEnable, + values = booleanOptions, + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.strategy)) }, + summary = { Text(stringResource(dnsEnabled.dnsStrategyTextRes)) }, + valueToText = { AnnotatedString(stringResource(it.dnsStrategyTextRes)) }, + ) + listPreference( + key = "dnsPreferH3", + value = configuration.dns.preferH3, + onValueChange = actions::updateDnsPreferH3, + values = booleanOptions, + modifier = Modifier.fillMaxWidth(), + enabled = dnsEnabled != false, + title = { Text(stringResource(R.string.prefer_h3)) }, + summary = { Text(stringResource(configuration.dns.preferH3.textRes)) }, + valueToText = { AnnotatedString(stringResource(it.textRes)) }, + ) + overrideEditTextPreferenceItem( + key = "dnsListen", + title = R.string.listen, + placeholder = R.string.dont_modify, + emptyLabel = R.string.disabled, + value = configuration.dns.listen, + onValueChange = actions::updateDnsListen, + enabled = dnsEnabled != false, + ) + listPreference( + key = "appendSystemDns", + value = configuration.app.appendSystemDns, + onValueChange = actions::updateAppendSystemDns, + values = booleanOptions, + modifier = Modifier.fillMaxWidth(), + enabled = dnsEnabled != false, + title = { Text(stringResource(R.string.append_system_dns)) }, + summary = { Text(stringResource(configuration.app.appendSystemDns.textRes)) }, + valueToText = { AnnotatedString(stringResource(it.textRes)) }, + ) + listPreference( + key = "dnsIpv6", + value = configuration.dns.ipv6, + onValueChange = actions::updateDnsIpv6, + values = booleanOptions, + modifier = Modifier.fillMaxWidth(), + enabled = dnsEnabled != false, + title = { Text(stringResource(R.string.ipv6)) }, + summary = { Text(stringResource(configuration.dns.ipv6.textRes)) }, + valueToText = { AnnotatedString(stringResource(it.textRes)) }, + ) + listPreference( + key = "dnsUseHosts", + value = configuration.dns.useHosts, + onValueChange = actions::updateDnsUseHosts, + values = booleanOptions, + modifier = Modifier.fillMaxWidth(), + enabled = dnsEnabled != false, + title = { Text(stringResource(R.string.use_hosts)) }, + summary = { Text(stringResource(configuration.dns.useHosts.textRes)) }, + valueToText = { AnnotatedString(stringResource(it.textRes)) }, + ) + listPreference( + key = "dnsEnhancedMode", + value = configuration.dns.enhancedMode, + onValueChange = actions::updateDnsEnhancedMode, + values = ConfigurationOverride.DnsEnhancedMode.entries, + modifier = Modifier.fillMaxWidth(), + enabled = dnsEnabled != false, + title = { Text(stringResource(R.string.enhanced_mode)) }, + summary = { Text(stringResource(configuration.dns.enhancedMode.textRes)) }, + valueToText = { AnnotatedString(stringResource(it.textRes)) }, + ) + preference( + key = "dnsNameServer", + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.name_server)) }, + summary = { Text(configuration.dns.nameServer.listSummary(R.string.dont_modify)) }, + enabled = dnsEnabled != false, + onClick = { + onOpenEditableTextList( + R.string.name_server, + configuration.dns.nameServer, + actions::updateDnsNameServer, + ) + }, + ) + preference( + key = "dnsFallback", + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.fallback)) }, + summary = { Text(configuration.dns.fallback.listSummary(R.string.dont_modify)) }, + enabled = dnsEnabled != false, + onClick = { + onOpenEditableTextList( + R.string.fallback, + configuration.dns.fallback, + actions::updateDnsFallback, + ) + }, + ) + preference( + key = "dnsDefaultServer", + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.default_name_server)) }, + summary = { Text(configuration.dns.defaultServer.listSummary(R.string.dont_modify)) }, + enabled = dnsEnabled != false, + onClick = { + onOpenEditableTextList( + R.string.default_name_server, + configuration.dns.defaultServer, + actions::updateDnsDefaultServer, + ) + }, + ) + preference( + key = "dnsFakeIpFilter", + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.fakeip_filter)) }, + summary = { Text(configuration.dns.fakeIpFilter.listSummary(R.string.dont_modify)) }, + enabled = dnsEnabled != false, + onClick = { + onOpenEditableTextList( + R.string.fakeip_filter, + configuration.dns.fakeIpFilter, + actions::updateDnsFakeIpFilter, + ) + }, + ) + listPreference( + key = "dnsFakeIpFilterMode", + value = configuration.dns.fakeIPFilterMode, + onValueChange = actions::updateDnsFakeIpFilterMode, + values = ConfigurationOverride.FilterMode.entries, + modifier = Modifier.fillMaxWidth(), + enabled = dnsEnabled != false, + title = { Text(stringResource(R.string.fakeip_filter_mode)) }, + summary = { Text(stringResource(configuration.dns.fakeIPFilterMode.textRes)) }, + valueToText = { AnnotatedString(stringResource(it.textRes)) }, + ) + listPreference( + key = "dnsGeoIpFallback", + value = configuration.dns.fallbackFilter.geoIp, + onValueChange = actions::updateDnsGeoIpFallback, + values = booleanOptions, + modifier = Modifier.fillMaxWidth(), + enabled = dnsEnabled != false, + title = { Text(stringResource(R.string.geoip_fallback)) }, + summary = { Text(stringResource(configuration.dns.fallbackFilter.geoIp.textRes)) }, + valueToText = { AnnotatedString(stringResource(it.textRes)) }, + ) + overrideEditTextPreferenceItem( + key = "dnsGeoIpCode", + title = R.string.geoip_fallback_code, + placeholder = R.string.dont_modify, + emptyLabel = R.string.raw_cn, + value = configuration.dns.fallbackFilter.geoIpCode, + onValueChange = actions::updateDnsGeoIpCode, + enabled = dnsEnabled != false, + ) + preference( + key = "dnsDomainFallback", + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.domain_fallback)) }, + summary = { Text(configuration.dns.fallbackFilter.domain.listSummary(R.string.dont_modify)) }, + enabled = dnsEnabled != false, + onClick = { + onOpenEditableTextList( + R.string.domain_fallback, + configuration.dns.fallbackFilter.domain, + actions::updateDnsDomainFallback, + ) + }, + ) + preference( + key = "dnsIpcidrFallback", + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.ipcidr_fallback)) }, + summary = { Text(configuration.dns.fallbackFilter.ipcidr.listSummary(R.string.dont_modify)) }, + enabled = dnsEnabled != false, + onClick = { + onOpenEditableTextList( + R.string.ipcidr_fallback, + configuration.dns.fallbackFilter.ipcidr, + actions::updateDnsIpcidrFallback, + ) + }, + ) + preference( + key = "dnsNameserverPolicy", + modifier = Modifier.fillMaxWidth(), + title = { Text(stringResource(R.string.name_server_policy)) }, + summary = { Text(configuration.dns.nameserverPolicy.summary(R.string.dont_modify)) }, + enabled = dnsEnabled != false, + onClick = { + onOpenEditableTextMap( + R.string.name_server_policy, + configuration.dns.nameserverPolicy, + actions::updateDnsNameserverPolicy, + ) + }, + ) } -@Composable -private fun OverrideEditTextPreferenceItem( +private fun LazyListScope.overrideEditTextPreferenceItem( + key: String, @StringRes title: Int, @StringRes placeholder: Int, @StringRes emptyLabel: Int, @@ -631,80 +598,82 @@ private fun OverrideEditTextPreferenceItem( enabled: Boolean = true, numericOnly: Boolean = false, ) { - var showDialog by remember { mutableStateOf(false) } - val summary = - when { - value == null -> stringResource(placeholder) - value.isEmpty() -> stringResource(emptyLabel) - else -> value - } - Preference( - modifier = Modifier.fillMaxWidth(), - title = { Text(stringResource(title)) }, - summary = { Text(summary) }, - enabled = enabled, - onClick = { showDialog = true }, - ) - if (showDialog) { - var inputText by - remember(value) { - mutableStateOf( - TextFieldValue(text = value.orEmpty(), selection = TextRange(value.orEmpty().length)) - ) + item(key = key, contentType = "EditTextPreference") { + var showDialog by remember { mutableStateOf(false) } + val summary = + when { + value == null -> stringResource(placeholder) + value.isEmpty() -> stringResource(emptyLabel) + else -> value } - val focusRequester = remember { FocusRequester() } - val keyboardController = LocalSoftwareKeyboardController.current - LaunchedEffect(Unit) { - focusRequester.requestFocus() - keyboardController?.show() - } - AlertDialog( - onDismissRequest = { showDialog = false }, + Preference( + modifier = Modifier.fillMaxWidth(), title = { Text(stringResource(title)) }, - text = { - OutlinedTextField( - value = inputText, - onValueChange = { inputText = if (numericOnly) it.filterDigits() else it }, - keyboardOptions = - if (numericOnly) { - KeyboardOptions(keyboardType = KeyboardType.Number) - } else { - KeyboardOptions.Default - }, - singleLine = true, - modifier = Modifier.fillMaxWidth().focusRequester(focusRequester), - ) - }, - confirmButton = { - TextButton( - onClick = { - onValueChange( + summary = { Text(summary) }, + enabled = enabled, + onClick = { showDialog = true }, + ) + if (showDialog) { + var inputText by + remember(value) { + mutableStateOf( + TextFieldValue(text = value.orEmpty(), selection = TextRange(value.orEmpty().length)) + ) + } + val focusRequester = remember { FocusRequester() } + val keyboardController = LocalSoftwareKeyboardController.current + LaunchedEffect(Unit) { + focusRequester.requestFocus() + keyboardController?.show() + } + AlertDialog( + onDismissRequest = { showDialog = false }, + title = { Text(stringResource(title)) }, + text = { + OutlinedTextField( + value = inputText, + onValueChange = { inputText = if (numericOnly) it.filterDigits() else it }, + keyboardOptions = if (numericOnly) { - portText(parsePort(inputText.text)) + KeyboardOptions(keyboardType = KeyboardType.Number) } else { - inputText.text - } - ) - showDialog = false - } - ) { - Text(stringResource(R.string.ok)) - } - }, - dismissButton = { - Row { + KeyboardOptions.Default + }, + singleLine = true, + modifier = Modifier.fillMaxWidth().focusRequester(focusRequester), + ) + }, + confirmButton = { TextButton( onClick = { - onValueChange(null) + onValueChange( + if (numericOnly) { + portText(parsePort(inputText.text)) + } else { + inputText.text + } + ) showDialog = false } ) { - Text(stringResource(R.string.reset)) + Text(stringResource(R.string.ok)) } - TextButton(onClick = { showDialog = false }) { Text(stringResource(R.string.cancel)) } - } - }, - ) + }, + dismissButton = { + Row { + TextButton( + onClick = { + onValueChange(null) + showDialog = false + } + ) { + Text(stringResource(R.string.reset)) + } + TextButton(onClick = { showDialog = false }) { Text(stringResource(R.string.cancel)) } + } + }, + ) + } } } diff --git a/app/src/main/kotlin/com/github/kr328/clash/settings/ui/PreferenceExtensions.kt b/app/src/main/kotlin/com/github/kr328/clash/settings/ui/PreferenceExtensions.kt new file mode 100644 index 0000000000..51cbefe174 --- /dev/null +++ b/app/src/main/kotlin/com/github/kr328/clash/settings/ui/PreferenceExtensions.kt @@ -0,0 +1,61 @@ +// TODO: https://github.com/zhanghai/ComposePreference/pull/34 +@file:Suppress("PackageDirectoryMismatch", "NOTHING_TO_INLINE") + +package me.zhanghai.compose.preference + +import androidx.compose.foundation.lazy.LazyListScope +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.AnnotatedString + +inline fun LazyListScope.listPreference( + key: String, + value: T, + noinline onValueChange: (T) -> Unit, + values: List, + noinline title: @Composable () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + noinline icon: @Composable (() -> Unit)? = null, + noinline summary: @Composable (() -> Unit)? = null, + type: ListPreferenceType = ListPreferenceType.ALERT_DIALOG, + noinline valueToText: @Composable (T) -> AnnotatedString = { AnnotatedString(it.toString()) }, +) { + item(key = key, contentType = "ListPreference") { + ListPreference( + value = value, + onValueChange = onValueChange, + values = values, + title = title, + modifier = modifier, + enabled = enabled, + icon = icon, + summary = summary, + type = type, + valueToText = valueToText, + ) + } +} + +inline fun LazyListScope.switchPreference( + key: String, + value: Boolean, + noinline onValueChange: (Boolean) -> Unit, + noinline title: @Composable () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + noinline icon: @Composable (() -> Unit)? = null, + noinline summary: @Composable (() -> Unit)? = null, +) { + item(key = key, contentType = "SwitchPreference") { + SwitchPreference( + value = value, + onValueChange = onValueChange, + title = title, + modifier = modifier, + enabled = enabled, + icon = icon, + summary = summary, + ) + } +}