Use ruleset IDs for routing edits

Switch routing ruleset lookup, save, and delete flows from list index to stable ruleset ID. Routing edit intents now pass `ruleset_id`, editing reloads by ID, and list item edit actions use each rule’s ID. This avoids index mismatch bugs when list order changes and keeps update/delete targeting the correct ruleset.
This commit is contained in:
2dust committed 2026-08-30 19:38:43 +08:00
1 parent 258de20972
commit c548e5f615
3 files changed
+60 -44

No files matched your search

@@ -123,17 +123,14 @@ object SettingsManager {
}
/**
* Get a routing ruleset by index.
* @param index The index of the ruleset.
* @return The RulesetItem.
*/
fun getRoutingRuleset(index: Int): RulesetItem? {
if (index < 0) return null
* Get a routing ruleset by id.
* @param id The id of the ruleset.
* @return The RulesetItem.
*/
fun getRoutingRuleset(index: String?): RulesetItem? {
if (index.isNullOrEmpty()) return null
val rulesetList = MmkvManager.decodeRoutingRulesets()
if (rulesetList.isNullOrEmpty()) return null
return rulesetList[index]
return MmkvManager.decodeRoutingRulesets()?.firstOrNull { it.id == index }
}
/**
@@ -142,33 +139,48 @@ object SettingsManager {
* @param ruleset The RulesetItem to save.
*/
fun saveRoutingRuleset(index: Int, ruleset: RulesetItem?) {
if (ruleset == null) return
if (ruleset == null) return
var rulesetList = MmkvManager.decodeRoutingRulesets()
if (rulesetList.isNullOrEmpty()) {
rulesetList = mutableListOf()
var rulesetList = MmkvManager.decodeRoutingRulesets()
if (rulesetList.isNullOrEmpty()) {
rulesetList = mutableListOf()
}
if (index < 0 || index >= rulesetList.count()) {
rulesetList.add(0, ruleset)
} else {
rulesetList[index] = ruleset
}
MmkvManager.encodeRoutingRulesets(rulesetList)
if (index < 0 || index >= rulesetList.count()) {
rulesetList.add(0, ruleset)
} else {
rulesetList[index] = ruleset
}
MmkvManager.encodeRoutingRulesets(rulesetList)
}
/**
* Remove a routing ruleset by index.
* @param index The index of the ruleset.
*/
fun removeRoutingRuleset(index: Int) {
if (index < 0) return
fun saveRoutingRuleset(index: String?, ruleset: RulesetItem?) {
if (ruleset == null) return
if (ruleset.id.isBlank()) {
ruleset.id = java.util.UUID.randomUUID().toString()
}
val rulesetList = MmkvManager.decodeRoutingRulesets()
if (rulesetList.isNullOrEmpty()) return
val rulesetList = MmkvManager.decodeRoutingRulesets()?.toMutableList() ?: mutableListOf()
val targetId = if (index.isNullOrEmpty()) ruleset.id else index
val index = rulesetList.indexOfFirst { it.id == targetId }
rulesetList.removeAt(index)
MmkvManager.encodeRoutingRulesets(rulesetList)
if (index >= 0) {
rulesetList[index] = ruleset
} else {
rulesetList.add(0, ruleset)
}
MmkvManager.encodeRoutingRulesets(rulesetList)
}
fun removeRoutingRuleset(index: String?) {
if (index.isNullOrEmpty()) return
val rulesetList = MmkvManager.decodeRoutingRulesets() ?: return
val targetIndex = rulesetList.indexOfFirst { it.id == index }
if (targetIndex < 0) return
rulesetList.removeAt(targetIndex)
MmkvManager.encodeRoutingRulesets(rulesetList)
}
/**
@@ -57,7 +57,7 @@ import java.util.UUID
private val ROUTING_NETWORK_OPTIONS = listOf("tcp", "udp", "tcp,udp")
class RoutingEditActivity : BaseComponentActivity() {
private val position by lazy { intent.getIntExtra("position", -1) }
private val rulesetId by lazy { intent.getStringExtra("ruleset_id") }
private var initial: RulesetItem? = null
private lateinit var outboundSuggestions: List<String>
@@ -65,7 +65,11 @@ class RoutingEditActivity : BaseComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
initial = SettingsManager.getRoutingRuleset(position)
initial = rulesetId?.let { SettingsManager.getRoutingRuleset(it) }
if (initial == null) {
finish()
return
}
val profileRemarks = SettingsManager.getProfileRemarks()
outboundSuggestions = (BUILTIN_OUTBOUND_TAGS.toList() + profileRemarks).distinct()
canUseProcess = SettingsManager.canUseProcessRouting()
@@ -74,7 +78,7 @@ class RoutingEditActivity : BaseComponentActivity() {
@Composable
override fun ScreenContent() {
RoutingEditScreen(
position = position,
rulesetId = rulesetId,
initial = initial,
outboundSuggestions = outboundSuggestions,
canUseProcess = canUseProcess,
@@ -89,19 +93,19 @@ class RoutingEditActivity : BaseComponentActivity() {
toast(R.string.sub_setting_remarks)
return false
}
if (position < 0 && rulesetItem.id.isEmpty()) {
if (rulesetItem.id.isEmpty()) {
rulesetItem.id = UUID.randomUUID().toString()
}
SettingsManager.saveRoutingRuleset(position, rulesetItem)
SettingsManager.saveRoutingRuleset(rulesetId, rulesetItem)
toastSuccess(R.string.toast_success)
finish()
return true
}
private fun deleteServer(): Boolean {
if (position >= 0) {
if (!rulesetId.isNullOrEmpty()) {
lifecycleScope.launch(Dispatchers.IO) {
SettingsManager.removeRoutingRuleset(position)
SettingsManager.removeRoutingRuleset(rulesetId)
withContext(Dispatchers.Main) { finish() }
}
}
@@ -111,7 +115,7 @@ class RoutingEditActivity : BaseComponentActivity() {
@Composable
fun RoutingEditScreen(
position: Int,
rulesetId: String?,
initial: RulesetItem?,
outboundSuggestions: List<String>,
canUseProcess: Boolean,
@@ -147,7 +151,7 @@ fun RoutingEditScreen(
}
fun buildRuleset(): RulesetItem {
val rulesetItem = SettingsManager.getRoutingRuleset(position) ?: RulesetItem()
val rulesetItem = SettingsManager.getRoutingRuleset(rulesetId) ?: RulesetItem()
rulesetItem.apply {
this.remarks = remarks
this.locked = locked
@@ -185,7 +189,7 @@ fun RoutingEditScreen(
title = stringResource(R.string.routing_settings_rule_title),
onBackClick = onBackClick,
actions = {
if (position >= 0) {
if (initial != null) {
IconButton(onClick = { showDeleteConfirm = true }) {
Icon(
painterResource(R.drawable.ic_delete_24dp),
@@ -101,8 +101,8 @@ class RoutingSettingActivity : HelperBaseComponentActivity() {
domainStrategyState = domainStrategyState,
onBackClick = { finish() },
onAddRule = { startActivity(Intent(this, RoutingEditActivity::class.java)) },
onEditRule = { position ->
startActivity(Intent(this, RoutingEditActivity::class.java).putExtra("position", position))
onEditRule = { rulesetId ->
startActivity(Intent(this, RoutingEditActivity::class.java).putExtra("ruleset_id", rulesetId))
},
onDomainStrategySelected = { value ->
MmkvManager.encodeSettings(AppConfig.PREF_ROUTING_DOMAIN_STRATEGY, value)
@@ -194,7 +194,7 @@ fun RoutingSettingScreen(
domainStrategyState: MutableStateFlow<String>,
onBackClick: () -> Unit,
onAddRule: () -> Unit,
onEditRule: (Int) -> Unit,
onEditRule: (String) -> Unit,
onDomainStrategySelected: (String) -> Unit,
onImportPredefined: (RoutingType) -> Unit,
onImportClipboard: () -> Unit,
@@ -291,7 +291,7 @@ fun RoutingSettingScreen(
) {
RoutingRulesetItem(
ruleset = ruleset,
onEdit = { onEditRule(index) },
onEdit = { onEditRule(ruleset.id) },
onEnabledChange = { checked ->
val updated = ruleset.copy(enabled = checked)
viewModel.update(index, updated)