From 405cd7f55e513f455f62aae16ceb235f2163e2f3 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Mon, 9 Mar 2026 19:38:29 +0800 Subject: [PATCH] Ensure default subscription on removals Add removeSubscriptionWithDefault in SettingsManager to remove a subscription and recreate a default subscription if none remain. Modify MmkvManager.decodeAllServerList to include servers from DEFAULT_SUBSCRIPTION_ID when it's not listed, and remove the defensive check in MmkvManager.removeSubscription so removals are delegated to SettingsManager. Update callers (SubEditActivity, SubscriptionsViewModel) to use SettingsManager.removeSubscriptionWithDefault, remove UI restrictions hiding the delete action for the default subscription, and simplify group-display logic in MainViewModel. These changes ensure a default subscription always exists and server lists include default servers when appropriate. --- .../java/com/v2ray/ang/handler/MmkvManager.kt | 15 +++++----- .../com/v2ray/ang/handler/SettingsManager.kt | 30 ++++++++++++++++++- .../java/com/v2ray/ang/ui/SubEditActivity.kt | 9 ++---- .../v2ray/ang/ui/SubSettingRecyclerAdapter.kt | 1 - .../com/v2ray/ang/viewmodel/MainViewModel.kt | 4 +-- .../ang/viewmodel/SubscriptionsViewModel.kt | 2 +- 6 files changed, 42 insertions(+), 19 deletions(-) diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/MmkvManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/MmkvManager.kt index 8a9ea0cb..c33b9ebd 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/MmkvManager.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/MmkvManager.kt @@ -114,9 +114,15 @@ object MmkvManager { */ fun decodeAllServerList(): MutableList { val allServers = mutableListOf() + val subsList = decodeSubsList() - // Add servers from all subscriptions (including default subscription) - decodeSubsList().forEach { guid -> + // If DEFAULT_SUBSCRIPTION_ID is not in the subscriptions list, add its servers + if (!subsList.contains(DEFAULT_SUBSCRIPTION_ID)) { + allServers.addAll(decodeServerList(DEFAULT_SUBSCRIPTION_ID)) + } + + // Add servers from all subscriptions + subsList.forEach { guid -> allServers.addAll(decodeServerList(guid)) } @@ -381,11 +387,6 @@ object MmkvManager { * @param subid The subscription ID. */ fun removeSubscription(subid: String) { - // Protect default subscription from being deleted - if (subid == DEFAULT_SUBSCRIPTION_ID) { - return - } - subStorage.remove(subid) val subsList = decodeSubsList() subsList.remove(subid) diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SettingsManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SettingsManager.kt index 52358a07..0acb5f45 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SettingsManager.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SettingsManager.kt @@ -25,6 +25,7 @@ import com.v2ray.ang.handler.MmkvManager.decodeServerConfig import com.v2ray.ang.handler.MmkvManager.decodeSubsList import com.v2ray.ang.handler.MmkvManager.decodeSubscription import com.v2ray.ang.handler.MmkvManager.encodeSubscription +import com.v2ray.ang.handler.MmkvManager.removeSubscription import com.v2ray.ang.util.JsonUtil import com.v2ray.ang.util.Utils import java.io.File @@ -36,7 +37,7 @@ object SettingsManager { fun initApp(context: Context) { ensureDefaultSettings() - ensureDefaultSubscription() + //ensureDefaultSubscription() initRoutingRulesets(context) migrateServerListToSubscriptions() migrateHysteria2PinSHA256() @@ -240,6 +241,33 @@ object SettingsManager { .firstOrNull { it.remarks == remarks } } + /** + * Removes the subscription. + * If there are no remaining subscriptions, + * it creates a new default subscription to ensure that ungroup + **/ + fun removeSubscriptionWithDefault(subid: String) { +// val subsList = decodeSubsList() +// if (subsList.size == 1 && subsList.first() == DEFAULT_SUBSCRIPTION_ID) { +// Log.i(ANG_PACKAGE,"Attempted to remove the only existing default subscription, operation ignored.") +// return +// } + + // Remove the subscription + removeSubscription(subid) + + // After removal, check if there are any subscriptions left. If not, create a default subscription. + val subsList2 = decodeSubsList() + if (subsList2.isNotEmpty()) { + return + } + + val defaultSub = SubscriptionItem( + remarks = "Default", + ) + encodeSubscription(DEFAULT_SUBSCRIPTION_ID, defaultSub) + } + /** * Get the SOCKS port. * @return The SOCKS port. diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/SubEditActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/SubEditActivity.kt index 5c7db83e..c6bd3870 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/SubEditActivity.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/SubEditActivity.kt @@ -14,6 +14,7 @@ import com.v2ray.ang.extension.toast import com.v2ray.ang.extension.toastSuccess import com.v2ray.ang.handler.MmkvManager import com.v2ray.ang.handler.SettingsChangeManager +import com.v2ray.ang.handler.SettingsManager import com.v2ray.ang.util.Utils import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -118,7 +119,7 @@ class SubEditActivity : BaseActivity() { AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm) .setPositiveButton(android.R.string.ok) { _, _ -> lifecycleScope.launch(Dispatchers.IO) { - MmkvManager.removeSubscription(editSubId) + SettingsManager.removeSubscriptionWithDefault(editSubId) launch(Dispatchers.Main) { finish() } @@ -130,7 +131,7 @@ class SubEditActivity : BaseActivity() { .show() } else { lifecycleScope.launch(Dispatchers.IO) { - MmkvManager.removeSubscription(editSubId) + SettingsManager.removeSubscriptionWithDefault(editSubId) launch(Dispatchers.Main) { finish() } @@ -145,10 +146,6 @@ class SubEditActivity : BaseActivity() { del_config = menu.findItem(R.id.del_config) save_config = menu.findItem(R.id.save_config) - if (editSubId.isEmpty() || editSubId == AppConfig.DEFAULT_SUBSCRIPTION_ID) { - del_config?.isVisible = false - } - return super.onCreateOptionsMenu(menu) } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/SubSettingRecyclerAdapter.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/SubSettingRecyclerAdapter.kt index 164fba6a..311c3b17 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/SubSettingRecyclerAdapter.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/SubSettingRecyclerAdapter.kt @@ -39,7 +39,6 @@ class SubSettingRecyclerAdapter( holder.itemSubSettingBinding.layoutRemove.setOnClickListener { adapterListener?.onRemove(subId, position) } - holder.itemSubSettingBinding.layoutRemove.isVisible = subId != AppConfig.DEFAULT_SUBSCRIPTION_ID holder.itemSubSettingBinding.chkEnable.setOnCheckedChangeListener { it, isChecked -> if (!it.isPressed) return@setOnCheckedChangeListener diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/MainViewModel.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/MainViewModel.kt index 3f2042da..0f56d56a 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/MainViewModel.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/MainViewModel.kt @@ -252,9 +252,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { } val groups = mutableListOf() - if (subscriptions.size > 1 - && MmkvManager.decodeSettingsBool(AppConfig.PREF_GROUP_ALL_DISPLAY) - ) { + if (MmkvManager.decodeSettingsBool(AppConfig.PREF_GROUP_ALL_DISPLAY)) { groups.add( GroupMapItem( id = "", diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/SubscriptionsViewModel.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/SubscriptionsViewModel.kt index a8c720ab..b182f1d8 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/SubscriptionsViewModel.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/SubscriptionsViewModel.kt @@ -21,7 +21,7 @@ class SubscriptionsViewModel : ViewModel() { fun remove(subId: String): Boolean { val changed = subscriptions.removeAll { it.guid == subId } if (changed) { - MmkvManager.removeSubscription(subId) + SettingsManager.removeSubscriptionWithDefault(subId) SettingsChangeManager.makeSetupGroupTab() } return changed