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 8a9ea0cb9..c33b9ebd6 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 52358a079..0acb5f452 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 5c7db83ec..c6bd38705 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 164fba6af..311c3b173 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 3f2042dab..0f56d56ac 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 a8c720ab5..b182f1d82 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