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
+110 -9

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,7 +184,12 @@ fun SettingsScreen(
.verticalScrollbar(scrollState)
.verticalScroll(scrollState)
) {
PreferenceGroupHeader(title = stringResource(R.string.title_ui_settings))
CollapsiblePreferenceGroupHeader(
title = stringResource(R.string.title_ui_settings),
expanded = uiSettingsExpanded,
onExpandedChange = { uiSettingsExpanded = it }
)
if (uiSettingsExpanded) {
SettingsSwitchItem(
title = stringResource(R.string.title_pref_speed_enabled),
summary = stringResource(R.string.summary_pref_speed_enabled),
@@ -222,8 +237,14 @@ fun SettingsScreen(
ThemeManager.setThemeMode(it)
}
)
}
PreferenceGroupHeader(title = stringResource(R.string.title_vpn_settings))
CollapsiblePreferenceGroupHeader(
title = stringResource(R.string.title_vpn_settings),
expanded = vpnSettingsExpanded,
onExpandedChange = { vpnSettingsExpanded = it }
)
if (vpnSettingsExpanded) {
SettingsSwitchItem(
title = stringResource(R.string.title_pref_ipv6_enabled),
summary = stringResource(R.string.summary_pref_ipv6_enabled),
@@ -313,8 +334,14 @@ fun SettingsScreen(
keyboardNumber = true,
onValueChanged = { hevTunRwTimeout = it }
)
}
PreferenceGroupHeader(title = stringResource(R.string.title_core_settings))
CollapsiblePreferenceGroupHeader(
title = stringResource(R.string.title_core_settings),
expanded = coreSettingsExpanded,
onExpandedChange = { coreSettingsExpanded = it }
)
if (coreSettingsExpanded) {
SettingsSwitchItem(
title = stringResource(R.string.title_pref_sniffing_enabled),
summary = stringResource(R.string.summary_pref_sniffing_enabled),
@@ -411,8 +438,14 @@ fun SettingsScreen(
selectedValue = outboundResolveMethod,
onSelected = { outboundResolveMethod = it }
)
}
PreferenceGroupHeader(title = stringResource(R.string.title_mux_settings))
CollapsiblePreferenceGroupHeader(
title = stringResource(R.string.title_mux_settings),
expanded = muxSettingsExpanded,
onExpandedChange = { muxSettingsExpanded = it }
)
if (muxSettingsExpanded) {
SettingsSwitchItem(
title = stringResource(R.string.title_pref_mux_enabled),
summary = stringResource(R.string.summary_pref_mux_enabled),
@@ -441,8 +474,14 @@ fun SettingsScreen(
enabled = mux && muxXudpConcurrencyInt >= 0,
onSelected = { muxXudpQuic = it }
)
}
PreferenceGroupHeader(title = stringResource(R.string.title_fragment_settings))
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,
@@ -475,8 +514,14 @@ fun SettingsScreen(
keyboardNumber = true,
onValueChanged = { fragmentMaxSplit = it }
)
}
PreferenceGroupHeader(title = stringResource(R.string.title_observatory_settings))
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,
@@ -521,8 +566,14 @@ fun SettingsScreen(
}
}
)
}
PreferenceGroupHeader(title = stringResource(R.string.title_advanced))
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),
@@ -545,8 +596,14 @@ fun SettingsScreen(
value = ipApiUrl,
onValueChanged = { ipApiUrl = it }
)
}
PreferenceGroupHeader(title = stringResource(R.string.title_mode_settings))
CollapsiblePreferenceGroupHeader(
title = stringResource(R.string.title_mode_settings),
expanded = modeSettingsExpanded,
onExpandedChange = { modeSettingsExpanded = it }
)
if (modeSettingsExpanded) {
SettingsListItem(
title = stringResource(R.string.title_mode),
entries = modeEntries,
@@ -586,6 +643,7 @@ fun SettingsScreen(
}
}
)
}
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>