Make settings groups collapsible (#5939)

This commit is contained in:
eliotcougar authored and GitHub committed 2026-07-23 20:59:55 +08:00
1 parent c9e1501d39
commit ce75aad55e
3 files changed
+500 -399

No files matched your search

@@ -21,11 +21,14 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.draw.scale
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.unit.dp
import com.v2ray.ang.R
@Composable
fun PreferenceGroupHeader(title: String, modifier: Modifier = Modifier) {
@@ -39,6 +42,37 @@ fun PreferenceGroupHeader(title: String, modifier: Modifier = Modifier) {
)
}
@Composable
fun CollapsiblePreferenceGroupHeader(
title: String,
expanded: Boolean,
onExpandedChange: (Boolean) -> Unit,
modifier: Modifier = Modifier
) {
Row(
modifier = modifier
.fillMaxWidth()
.clickable { onExpandedChange(!expanded) }
.padding(horizontal = 16.dp, vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = title,
style = MaterialTheme.typography.titleSmall,
color = colorFabActive,
modifier = Modifier.weight(1f)
)
Icon(
painter = painterResource(R.drawable.ic_expand_more_24dp),
contentDescription = null,
tint = colorFabActive,
modifier = Modifier
.size(24.dp)
.rotate(if (expanded) 180f else 0f)
)
}
}
@Composable
private fun SettingsItemRow(
icon: Painter?,
@@ -14,6 +14,8 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.ScaffoldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringArrayResource
@@ -24,7 +26,7 @@ import com.v2ray.ang.AppConfig
import com.v2ray.ang.AppConfig.VPN
import com.v2ray.ang.R
import com.v2ray.ang.compose.AppTopBar
import com.v2ray.ang.compose.PreferenceGroupHeader
import com.v2ray.ang.compose.CollapsiblePreferenceGroupHeader
import com.v2ray.ang.compose.SettingsEditItem
import com.v2ray.ang.compose.SettingsListItem
import com.v2ray.ang.compose.SettingsMenuItem
@@ -65,6 +67,14 @@ fun SettingsScreen(
) {
val scrollState = rememberScrollState()
val isLoading by viewModel.isLoading.collectAsStateWithLifecycle()
var uiSettingsExpanded by rememberSaveable { mutableStateOf(true) }
var vpnSettingsExpanded by rememberSaveable { mutableStateOf(true) }
var coreSettingsExpanded by rememberSaveable { mutableStateOf(true) }
var muxSettingsExpanded by rememberSaveable { mutableStateOf(false) }
var fragmentSettingsExpanded by rememberSaveable { mutableStateOf(false) }
var observatorySettingsExpanded by rememberSaveable { mutableStateOf(false) }
var advancedSettingsExpanded by rememberSaveable { mutableStateOf(false) }
var modeSettingsExpanded by rememberSaveable { mutableStateOf(false) }
var localDns by rememberMmkvBool(AppConfig.PREF_LOCAL_DNS_ENABLED, false)
var fakeDns by rememberMmkvBool(AppConfig.PREF_FAKE_DNS_ENABLED, false)
@@ -174,418 +184,466 @@ fun SettingsScreen(
.verticalScrollbar(scrollState)
.verticalScroll(scrollState)
) {
PreferenceGroupHeader(title = stringResource(R.string.title_ui_settings))
SettingsSwitchItem(
title = stringResource(R.string.title_pref_speed_enabled),
summary = stringResource(R.string.summary_pref_speed_enabled),
checked = speedEnabled,
onCheckedChange = { speedEnabled = it }
CollapsiblePreferenceGroupHeader(
title = stringResource(R.string.title_ui_settings),
expanded = uiSettingsExpanded,
onExpandedChange = { uiSettingsExpanded = it }
)
SettingsSwitchItem(
title = stringResource(R.string.title_pref_confirm_remove),
summary = stringResource(R.string.summary_pref_confirm_remove),
checked = confirmRemove,
onCheckedChange = { confirmRemove = it }
)
SettingsSwitchItem(
title = stringResource(R.string.title_pref_double_column_display),
summary = stringResource(R.string.summary_pref_double_column_display),
checked = doubleColumnDisplay,
onCheckedChange = {
doubleColumnDisplay = it
SettingsChangeManager.makeSetupGroupTab()
}
)
SettingsSwitchItem(
title = stringResource(R.string.title_pref_group_all_display),
summary = stringResource(R.string.summary_pref_group_all_display),
checked = groupAllDisplay,
onCheckedChange = {
groupAllDisplay = it
SettingsChangeManager.makeSetupGroupTab()
}
)
SettingsListItem(
title = stringResource(R.string.title_language),
entries = languageEntries,
values = languageValues,
selectedValue = language,
onSelected = { language = it }
)
SettingsListItem(
title = stringResource(R.string.title_pref_ui_mode_night),
entries = uiModeNightEntries,
values = uiModeNightValues,
selectedValue = uiModeNight,
onSelected = {
uiModeNight = it
ThemeManager.setThemeMode(it)
}
)
PreferenceGroupHeader(title = stringResource(R.string.title_vpn_settings))
SettingsSwitchItem(
title = stringResource(R.string.title_pref_ipv6_enabled),
summary = stringResource(R.string.summary_pref_ipv6_enabled),
checked = ipv6Enabled,
onCheckedChange = { ipv6Enabled = it }
)
SettingsSwitchItem(
title = stringResource(R.string.title_pref_prefer_ipv6),
summary = stringResource(R.string.summary_pref_prefer_ipv6),
checked = preferIpv6,
onCheckedChange = { preferIpv6 = it }
)
SettingsSwitchItem(
title = stringResource(R.string.title_pref_local_dns_enabled),
summary = stringResource(R.string.summary_pref_local_dns_enabled),
checked = localDns,
enabled = isVpn,
onCheckedChange = { localDns = it }
)
SettingsSwitchItem(
title = stringResource(R.string.title_pref_fake_dns_enabled),
summary = stringResource(R.string.summary_pref_fake_dns_enabled),
checked = fakeDns,
enabled = isVpn && localDns,
onCheckedChange = { fakeDns = it }
)
SettingsEditItem(
title = stringResource(R.string.title_pref_vpn_dns),
value = vpnDns,
enabled = isVpn && !localDns,
onValueChanged = { vpnDns = it }
)
SettingsSwitchItem(
title = stringResource(R.string.title_pref_append_http_proxy),
summary = stringResource(R.string.summary_pref_append_http_proxy),
checked = appendHttpProxy,
enabled = effectiveLocalProxy,
onCheckedChange = { appendHttpProxy = it }
)
SettingsListItem(
title = stringResource(R.string.title_pref_vpn_bypass_lan),
entries = bypassLanEntries,
values = bypassLanValues,
selectedValue = vpnBypassLan,
enabled = isVpn,
onSelected = { vpnBypassLan = it }
)
SettingsListItem(
title = stringResource(R.string.title_pref_vpn_interface_address),
entries = interfaceAddrEntries,
values = interfaceAddrValues,
selectedValue = vpnInterfaceAddress,
enabled = isVpn,
onSelected = { vpnInterfaceAddress = it }
)
SettingsEditItem(
title = stringResource(R.string.title_pref_vpn_mtu),
value = vpnMtu,
enabled = isVpn,
keyboardNumber = true,
onValueChanged = { vpnMtu = it }
)
SettingsSwitchItem(
title = stringResource(R.string.title_pref_use_hev_tunnel),
summary = stringResource(R.string.summary_pref_use_hev_tunnel),
checked = useHevTun,
enabled = isVpn,
onCheckedChange = {
useHevTun = it
if (it && !enableLocalProxy) {
enableLocalProxy = true
if (uiSettingsExpanded) {
SettingsSwitchItem(
title = stringResource(R.string.title_pref_speed_enabled),
summary = stringResource(R.string.summary_pref_speed_enabled),
checked = speedEnabled,
onCheckedChange = { speedEnabled = it }
)
SettingsSwitchItem(
title = stringResource(R.string.title_pref_confirm_remove),
summary = stringResource(R.string.summary_pref_confirm_remove),
checked = confirmRemove,
onCheckedChange = { confirmRemove = it }
)
SettingsSwitchItem(
title = stringResource(R.string.title_pref_double_column_display),
summary = stringResource(R.string.summary_pref_double_column_display),
checked = doubleColumnDisplay,
onCheckedChange = {
doubleColumnDisplay = it
SettingsChangeManager.makeSetupGroupTab()
}
}
)
SettingsListItem(
title = stringResource(R.string.title_pref_hev_tunnel_loglevel),
entries = hevLogEntries,
values = hevLogValues,
selectedValue = hevTunLogLevel,
enabled = hevTunEnabled,
onSelected = { hevTunLogLevel = it }
)
SettingsEditItem(
title = stringResource(R.string.title_pref_hev_tunnel_rw_timeout),
value = hevTunRwTimeout,
enabled = hevTunEnabled,
keyboardNumber = true,
onValueChanged = { hevTunRwTimeout = it }
)
)
SettingsSwitchItem(
title = stringResource(R.string.title_pref_group_all_display),
summary = stringResource(R.string.summary_pref_group_all_display),
checked = groupAllDisplay,
onCheckedChange = {
groupAllDisplay = it
SettingsChangeManager.makeSetupGroupTab()
}
)
SettingsListItem(
title = stringResource(R.string.title_language),
entries = languageEntries,
values = languageValues,
selectedValue = language,
onSelected = { language = it }
)
SettingsListItem(
title = stringResource(R.string.title_pref_ui_mode_night),
entries = uiModeNightEntries,
values = uiModeNightValues,
selectedValue = uiModeNight,
onSelected = {
uiModeNight = it
ThemeManager.setThemeMode(it)
}
)
}
PreferenceGroupHeader(title = stringResource(R.string.title_core_settings))
SettingsSwitchItem(
title = stringResource(R.string.title_pref_sniffing_enabled),
summary = stringResource(R.string.summary_pref_sniffing_enabled),
checked = sniffingEnabled,
onCheckedChange = { sniffingEnabled = it }
CollapsiblePreferenceGroupHeader(
title = stringResource(R.string.title_vpn_settings),
expanded = vpnSettingsExpanded,
onExpandedChange = { vpnSettingsExpanded = it }
)
SettingsSwitchItem(
title = stringResource(R.string.title_pref_route_only_enabled),
summary = stringResource(R.string.summary_pref_route_only_enabled),
checked = routeOnlyEnabled,
onCheckedChange = { routeOnlyEnabled = it }
)
SettingsSwitchItem(
title = stringResource(R.string.title_pref_enable_local_proxy),
summary = stringResource(R.string.summary_pref_enable_local_proxy),
checked = enableLocalProxy,
enabled = !localProxyForced,
onCheckedChange = {
if (!localProxyForced) {
enableLocalProxy = it
if (!it && appendHttpProxy) {
appendHttpProxy = false
if (vpnSettingsExpanded) {
SettingsSwitchItem(
title = stringResource(R.string.title_pref_ipv6_enabled),
summary = stringResource(R.string.summary_pref_ipv6_enabled),
checked = ipv6Enabled,
onCheckedChange = { ipv6Enabled = it }
)
SettingsSwitchItem(
title = stringResource(R.string.title_pref_prefer_ipv6),
summary = stringResource(R.string.summary_pref_prefer_ipv6),
checked = preferIpv6,
onCheckedChange = { preferIpv6 = it }
)
SettingsSwitchItem(
title = stringResource(R.string.title_pref_local_dns_enabled),
summary = stringResource(R.string.summary_pref_local_dns_enabled),
checked = localDns,
enabled = isVpn,
onCheckedChange = { localDns = it }
)
SettingsSwitchItem(
title = stringResource(R.string.title_pref_fake_dns_enabled),
summary = stringResource(R.string.summary_pref_fake_dns_enabled),
checked = fakeDns,
enabled = isVpn && localDns,
onCheckedChange = { fakeDns = it }
)
SettingsEditItem(
title = stringResource(R.string.title_pref_vpn_dns),
value = vpnDns,
enabled = isVpn && !localDns,
onValueChanged = { vpnDns = it }
)
SettingsSwitchItem(
title = stringResource(R.string.title_pref_append_http_proxy),
summary = stringResource(R.string.summary_pref_append_http_proxy),
checked = appendHttpProxy,
enabled = effectiveLocalProxy,
onCheckedChange = { appendHttpProxy = it }
)
SettingsListItem(
title = stringResource(R.string.title_pref_vpn_bypass_lan),
entries = bypassLanEntries,
values = bypassLanValues,
selectedValue = vpnBypassLan,
enabled = isVpn,
onSelected = { vpnBypassLan = it }
)
SettingsListItem(
title = stringResource(R.string.title_pref_vpn_interface_address),
entries = interfaceAddrEntries,
values = interfaceAddrValues,
selectedValue = vpnInterfaceAddress,
enabled = isVpn,
onSelected = { vpnInterfaceAddress = it }
)
SettingsEditItem(
title = stringResource(R.string.title_pref_vpn_mtu),
value = vpnMtu,
enabled = isVpn,
keyboardNumber = true,
onValueChanged = { vpnMtu = it }
)
SettingsSwitchItem(
title = stringResource(R.string.title_pref_use_hev_tunnel),
summary = stringResource(R.string.summary_pref_use_hev_tunnel),
checked = useHevTun,
enabled = isVpn,
onCheckedChange = {
useHevTun = it
if (it && !enableLocalProxy) {
enableLocalProxy = true
}
}
}
)
SettingsSwitchItem(
title = stringResource(R.string.title_pref_proxy_sharing_enabled),
summary = stringResource(R.string.summary_pref_proxy_sharing_enabled),
checked = proxySharing,
enabled = effectiveLocalProxy,
onCheckedChange = { proxySharing = it }
)
SettingsSwitchItem(
title = stringResource(R.string.title_pref_dynamic_socks_port),
summary = stringResource(R.string.summary_pref_dynamic_socks_port),
checked = dynamicSocksPort,
enabled = effectiveLocalProxy,
onCheckedChange = { dynamicSocksPort = it }
)
SettingsEditItem(
title = stringResource(R.string.title_pref_socks_port),
value = socksPort,
enabled = effectiveLocalProxy && !dynamicSocksPort,
keyboardNumber = true,
onValueChanged = { socksPort = it }
)
SettingsEditItem(
title = stringResource(R.string.title_pref_socks_username),
value = socksUsername,
enabled = effectiveLocalProxy,
onValueChanged = { socksUsername = it }
)
SettingsEditItem(
title = stringResource(R.string.title_pref_socks_password),
value = socksPassword,
enabled = effectiveLocalProxy,
isPassword = true,
onValueChanged = { socksPassword = it }
)
SettingsSwitchItem(
title = stringResource(R.string.title_pref_socks_enable_udp),
summary = stringResource(R.string.summary_pref_socks_enable_udp),
checked = socksEnableUdp,
enabled = effectiveLocalProxy,
onCheckedChange = { socksEnableUdp = it }
)
SettingsEditItem(
title = stringResource(R.string.title_pref_remote_dns),
value = remoteDns,
onValueChanged = { remoteDns = it }
)
SettingsEditItem(
title = stringResource(R.string.title_pref_domestic_dns),
value = domesticDns,
onValueChanged = { domesticDns = it }
)
SettingsEditItem(
title = stringResource(R.string.title_pref_dns_hosts),
value = dnsHosts,
onValueChanged = { dnsHosts = it }
)
SettingsListItem(
title = stringResource(R.string.title_core_loglevel),
entries = coreLogLevelEntries,
values = coreLogLevelValues,
selectedValue = coreLogLevel,
onSelected = { coreLogLevel = it }
)
SettingsListItem(
title = stringResource(R.string.title_outbound_domain_resolve_method),
entries = outboundResolveEntries,
values = outboundResolveValues,
selectedValue = outboundResolveMethod,
onSelected = { outboundResolveMethod = it }
)
)
SettingsListItem(
title = stringResource(R.string.title_pref_hev_tunnel_loglevel),
entries = hevLogEntries,
values = hevLogValues,
selectedValue = hevTunLogLevel,
enabled = hevTunEnabled,
onSelected = { hevTunLogLevel = it }
)
SettingsEditItem(
title = stringResource(R.string.title_pref_hev_tunnel_rw_timeout),
value = hevTunRwTimeout,
enabled = hevTunEnabled,
keyboardNumber = true,
onValueChanged = { hevTunRwTimeout = it }
)
}
PreferenceGroupHeader(title = stringResource(R.string.title_mux_settings))
SettingsSwitchItem(
title = stringResource(R.string.title_pref_mux_enabled),
summary = stringResource(R.string.summary_pref_mux_enabled),
checked = mux,
onCheckedChange = { mux = it }
CollapsiblePreferenceGroupHeader(
title = stringResource(R.string.title_core_settings),
expanded = coreSettingsExpanded,
onExpandedChange = { coreSettingsExpanded = it }
)
SettingsEditItem(
title = stringResource(R.string.title_pref_mux_concurency),
value = muxConcurrency,
enabled = mux,
keyboardNumber = true,
onValueChanged = { muxConcurrency = it }
)
SettingsEditItem(
title = stringResource(R.string.title_pref_mux_xudp_concurency),
value = muxXudpConcurrency,
enabled = mux,
keyboardNumber = true,
onValueChanged = { muxXudpConcurrency = it }
)
SettingsListItem(
title = stringResource(R.string.title_pref_mux_xudp_quic),
entries = xudpQuicEntries,
values = xudpQuicValues,
selectedValue = muxXudpQuic,
enabled = mux && muxXudpConcurrencyInt >= 0,
onSelected = { muxXudpQuic = it }
)
PreferenceGroupHeader(title = stringResource(R.string.title_fragment_settings))
SettingsSwitchItem(
title = stringResource(R.string.title_pref_fragment_enabled),
checked = fragment,
onCheckedChange = { fragment = it }
)
SettingsListItem(
title = stringResource(R.string.title_pref_fragment_packets),
entries = fragmentPacketsEntries,
values = fragmentPacketsValues,
selectedValue = fragmentPackets,
enabled = fragment,
onSelected = { fragmentPackets = it }
)
SettingsEditItem(
title = stringResource(R.string.title_pref_fragment_length),
value = fragmentLength,
enabled = fragment,
onValueChanged = { fragmentLength = it }
)
SettingsEditItem(
title = stringResource(R.string.title_pref_fragment_interval),
value = fragmentInterval,
enabled = fragment,
onValueChanged = { fragmentInterval = it }
)
SettingsEditItem(
title = stringResource(R.string.title_pref_fragment_maxsplit),
value = fragmentMaxSplit,
enabled = fragment,
keyboardNumber = true,
onValueChanged = { fragmentMaxSplit = it }
)
PreferenceGroupHeader(title = stringResource(R.string.title_observatory_settings))
SettingsEditItem(
title = stringResource(R.string.title_pref_observatory_least_ping_interval),
value = observatoryLeastPingInterval,
onValueChanged = {
viewModel.validateObservatoryDuration(it)?.let { value ->
observatoryLeastPingInterval = value
}
}
)
SettingsEditItem(
title = stringResource(R.string.title_pref_observatory_least_load_interval),
value = observatoryLeastLoadInterval,
onValueChanged = {
viewModel.validateObservatoryDuration(it)?.let { value ->
observatoryLeastLoadInterval = value
}
}
)
SettingsListItem(
title = stringResource(R.string.title_pref_observatory_least_load_method),
entries = observatoryLeastLoadMethodEntries,
values = observatoryLeastLoadMethodValues,
selectedValue = observatoryLeastLoadMethod,
onSelected = { observatoryLeastLoadMethod = it }
)
SettingsEditItem(
title = stringResource(R.string.title_pref_observatory_least_load_sampling),
value = observatoryLeastLoadSampling,
keyboardNumber = true,
onValueChanged = {
viewModel.validateObservatorySampling(it)?.let { value ->
observatoryLeastLoadSampling = value
}
}
)
SettingsEditItem(
title = stringResource(R.string.title_pref_observatory_least_load_timeout),
value = observatoryLeastLoadTimeout,
onValueChanged = {
viewModel.validateObservatoryDuration(it)?.let { value ->
observatoryLeastLoadTimeout = value
}
}
)
PreferenceGroupHeader(title = stringResource(R.string.title_advanced))
SettingsSwitchItem(
title = stringResource(R.string.title_pref_is_booted),
summary = stringResource(R.string.summary_pref_is_booted),
checked = isBooted,
onCheckedChange = { isBooted = it }
)
SettingsEditItem(
title = stringResource(R.string.title_pref_delay_test_url),
value = delayTestUrl,
onValueChanged = { delayTestUrl = it }
)
SettingsEditItem(
title = stringResource(R.string.title_pref_real_ping_concurrency),
value = realPingConcurrency,
keyboardNumber = true,
onValueChanged = { realPingConcurrency = it }
)
SettingsEditItem(
title = stringResource(R.string.title_pref_ip_api_url),
value = ipApiUrl,
onValueChanged = { ipApiUrl = it }
)
PreferenceGroupHeader(title = stringResource(R.string.title_mode_settings))
SettingsListItem(
title = stringResource(R.string.title_mode),
entries = modeEntries,
values = modeValues,
selectedValue = mode,
onSelected = { mode = it }
)
SettingsMenuItem(
title = stringResource(R.string.title_mode_help),
onClick = onModeHelpClicked
)
SettingsSwitchItem(
title = stringResource(R.string.title_root_mode_enabled),
summary = stringResource(R.string.summary_root_mode_enabled),
checked = enableRootMode,
onCheckedChange = { newValue ->
if (newValue && !RootManager.cachedRoot()) {
viewModel.checkAndRequestRoot {
enableRootMode = true
if (coreSettingsExpanded) {
SettingsSwitchItem(
title = stringResource(R.string.title_pref_sniffing_enabled),
summary = stringResource(R.string.summary_pref_sniffing_enabled),
checked = sniffingEnabled,
onCheckedChange = { sniffingEnabled = it }
)
SettingsSwitchItem(
title = stringResource(R.string.title_pref_route_only_enabled),
summary = stringResource(R.string.summary_pref_route_only_enabled),
checked = routeOnlyEnabled,
onCheckedChange = { routeOnlyEnabled = it }
)
SettingsSwitchItem(
title = stringResource(R.string.title_pref_enable_local_proxy),
summary = stringResource(R.string.summary_pref_enable_local_proxy),
checked = enableLocalProxy,
enabled = !localProxyForced,
onCheckedChange = {
if (!localProxyForced) {
enableLocalProxy = it
if (!it && appendHttpProxy) {
appendHttpProxy = false
}
}
} else {
enableRootMode = newValue
}
}
)
SettingsSwitchItem(
title = stringResource(R.string.title_pref_proxy_sharing_enabled),
summary = stringResource(R.string.summary_pref_proxy_sharing_enabled),
checked = proxySharing,
enabled = effectiveLocalProxy,
onCheckedChange = { proxySharing = it }
)
SettingsSwitchItem(
title = stringResource(R.string.title_pref_dynamic_socks_port),
summary = stringResource(R.string.summary_pref_dynamic_socks_port),
checked = dynamicSocksPort,
enabled = effectiveLocalProxy,
onCheckedChange = { dynamicSocksPort = it }
)
SettingsEditItem(
title = stringResource(R.string.title_pref_socks_port),
value = socksPort,
enabled = effectiveLocalProxy && !dynamicSocksPort,
keyboardNumber = true,
onValueChanged = { socksPort = it }
)
SettingsEditItem(
title = stringResource(R.string.title_pref_socks_username),
value = socksUsername,
enabled = effectiveLocalProxy,
onValueChanged = { socksUsername = it }
)
SettingsEditItem(
title = stringResource(R.string.title_pref_socks_password),
value = socksPassword,
enabled = effectiveLocalProxy,
isPassword = true,
onValueChanged = { socksPassword = it }
)
SettingsSwitchItem(
title = stringResource(R.string.title_pref_socks_enable_udp),
summary = stringResource(R.string.summary_pref_socks_enable_udp),
checked = socksEnableUdp,
enabled = effectiveLocalProxy,
onCheckedChange = { socksEnableUdp = it }
)
SettingsEditItem(
title = stringResource(R.string.title_pref_remote_dns),
value = remoteDns,
onValueChanged = { remoteDns = it }
)
SettingsEditItem(
title = stringResource(R.string.title_pref_domestic_dns),
value = domesticDns,
onValueChanged = { domesticDns = it }
)
SettingsEditItem(
title = stringResource(R.string.title_pref_dns_hosts),
value = dnsHosts,
onValueChanged = { dnsHosts = it }
)
SettingsListItem(
title = stringResource(R.string.title_core_loglevel),
entries = coreLogLevelEntries,
values = coreLogLevelValues,
selectedValue = coreLogLevel,
onSelected = { coreLogLevel = it }
)
SettingsListItem(
title = stringResource(R.string.title_outbound_domain_resolve_method),
entries = outboundResolveEntries,
values = outboundResolveValues,
selectedValue = outboundResolveMethod,
onSelected = { outboundResolveMethod = it }
)
}
CollapsiblePreferenceGroupHeader(
title = stringResource(R.string.title_mux_settings),
expanded = muxSettingsExpanded,
onExpandedChange = { muxSettingsExpanded = it }
)
SettingsSwitchItem(
title = stringResource(R.string.title_root_lan_sharing),
summary = stringResource(R.string.summary_root_lan_sharing),
checked = lanSharing,
onCheckedChange = { newValue ->
if (newValue && !RootManager.cachedRoot()) {
viewModel.checkAndRequestRoot {
lanSharing = true
if (muxSettingsExpanded) {
SettingsSwitchItem(
title = stringResource(R.string.title_pref_mux_enabled),
summary = stringResource(R.string.summary_pref_mux_enabled),
checked = mux,
onCheckedChange = { mux = it }
)
SettingsEditItem(
title = stringResource(R.string.title_pref_mux_concurency),
value = muxConcurrency,
enabled = mux,
keyboardNumber = true,
onValueChanged = { muxConcurrency = it }
)
SettingsEditItem(
title = stringResource(R.string.title_pref_mux_xudp_concurency),
value = muxXudpConcurrency,
enabled = mux,
keyboardNumber = true,
onValueChanged = { muxXudpConcurrency = it }
)
SettingsListItem(
title = stringResource(R.string.title_pref_mux_xudp_quic),
entries = xudpQuicEntries,
values = xudpQuicValues,
selectedValue = muxXudpQuic,
enabled = mux && muxXudpConcurrencyInt >= 0,
onSelected = { muxXudpQuic = it }
)
}
CollapsiblePreferenceGroupHeader(
title = stringResource(R.string.title_fragment_settings),
expanded = fragmentSettingsExpanded,
onExpandedChange = { fragmentSettingsExpanded = it }
)
if (fragmentSettingsExpanded) {
SettingsSwitchItem(
title = stringResource(R.string.title_pref_fragment_enabled),
checked = fragment,
onCheckedChange = { fragment = it }
)
SettingsListItem(
title = stringResource(R.string.title_pref_fragment_packets),
entries = fragmentPacketsEntries,
values = fragmentPacketsValues,
selectedValue = fragmentPackets,
enabled = fragment,
onSelected = { fragmentPackets = it }
)
SettingsEditItem(
title = stringResource(R.string.title_pref_fragment_length),
value = fragmentLength,
enabled = fragment,
onValueChanged = { fragmentLength = it }
)
SettingsEditItem(
title = stringResource(R.string.title_pref_fragment_interval),
value = fragmentInterval,
enabled = fragment,
onValueChanged = { fragmentInterval = it }
)
SettingsEditItem(
title = stringResource(R.string.title_pref_fragment_maxsplit),
value = fragmentMaxSplit,
enabled = fragment,
keyboardNumber = true,
onValueChanged = { fragmentMaxSplit = it }
)
}
CollapsiblePreferenceGroupHeader(
title = stringResource(R.string.title_observatory_settings),
expanded = observatorySettingsExpanded,
onExpandedChange = { observatorySettingsExpanded = it }
)
if (observatorySettingsExpanded) {
SettingsEditItem(
title = stringResource(R.string.title_pref_observatory_least_ping_interval),
value = observatoryLeastPingInterval,
onValueChanged = {
viewModel.validateObservatoryDuration(it)?.let { value ->
observatoryLeastPingInterval = value
}
} else {
lanSharing = newValue
}
}
)
SettingsEditItem(
title = stringResource(R.string.title_pref_observatory_least_load_interval),
value = observatoryLeastLoadInterval,
onValueChanged = {
viewModel.validateObservatoryDuration(it)?.let { value ->
observatoryLeastLoadInterval = value
}
}
)
SettingsListItem(
title = stringResource(R.string.title_pref_observatory_least_load_method),
entries = observatoryLeastLoadMethodEntries,
values = observatoryLeastLoadMethodValues,
selectedValue = observatoryLeastLoadMethod,
onSelected = { observatoryLeastLoadMethod = it }
)
SettingsEditItem(
title = stringResource(R.string.title_pref_observatory_least_load_sampling),
value = observatoryLeastLoadSampling,
keyboardNumber = true,
onValueChanged = {
viewModel.validateObservatorySampling(it)?.let { value ->
observatoryLeastLoadSampling = value
}
}
)
SettingsEditItem(
title = stringResource(R.string.title_pref_observatory_least_load_timeout),
value = observatoryLeastLoadTimeout,
onValueChanged = {
viewModel.validateObservatoryDuration(it)?.let { value ->
observatoryLeastLoadTimeout = value
}
}
)
}
CollapsiblePreferenceGroupHeader(
title = stringResource(R.string.title_advanced),
expanded = advancedSettingsExpanded,
onExpandedChange = { advancedSettingsExpanded = it }
)
if (advancedSettingsExpanded) {
SettingsSwitchItem(
title = stringResource(R.string.title_pref_is_booted),
summary = stringResource(R.string.summary_pref_is_booted),
checked = isBooted,
onCheckedChange = { isBooted = it }
)
SettingsEditItem(
title = stringResource(R.string.title_pref_delay_test_url),
value = delayTestUrl,
onValueChanged = { delayTestUrl = it }
)
SettingsEditItem(
title = stringResource(R.string.title_pref_real_ping_concurrency),
value = realPingConcurrency,
keyboardNumber = true,
onValueChanged = { realPingConcurrency = it }
)
SettingsEditItem(
title = stringResource(R.string.title_pref_ip_api_url),
value = ipApiUrl,
onValueChanged = { ipApiUrl = it }
)
}
CollapsiblePreferenceGroupHeader(
title = stringResource(R.string.title_mode_settings),
expanded = modeSettingsExpanded,
onExpandedChange = { modeSettingsExpanded = it }
)
if (modeSettingsExpanded) {
SettingsListItem(
title = stringResource(R.string.title_mode),
entries = modeEntries,
values = modeValues,
selectedValue = mode,
onSelected = { mode = it }
)
SettingsMenuItem(
title = stringResource(R.string.title_mode_help),
onClick = onModeHelpClicked
)
SettingsSwitchItem(
title = stringResource(R.string.title_root_mode_enabled),
summary = stringResource(R.string.summary_root_mode_enabled),
checked = enableRootMode,
onCheckedChange = { newValue ->
if (newValue && !RootManager.cachedRoot()) {
viewModel.checkAndRequestRoot {
enableRootMode = true
}
} else {
enableRootMode = newValue
}
}
)
SettingsSwitchItem(
title = stringResource(R.string.title_root_lan_sharing),
summary = stringResource(R.string.summary_root_lan_sharing),
checked = lanSharing,
onCheckedChange = { newValue ->
if (newValue && !RootManager.cachedRoot()) {
viewModel.checkAndRequestRoot {
lanSharing = true
}
} else {
lanSharing = newValue
}
}
)
}
Spacer(modifier = Modifier.height(24.dp))
}
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M7.41,8.59 12,13.17 16.59,8.59 18,10l-6,6 -6,-6z" />
</vector>