From 4812f067dbd9cfd811f5af99d31753cb400312e3 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Tue, 30 Dec 2025 16:16:32 +0800
Subject: [PATCH] Add policy group config type and UI support
Introduces a new POLICYGROUP config type for server grouping and selection. Adds ServerGroupActivity and related UI, updates menu options, and removes legacy intelligent selection logic. Refactors config management and filtering to support policy groups, and updates translations and resources accordingly.
---
V2rayNG/app/src/main/AndroidManifest.xml | 4 +
.../src/main/java/com/v2ray/ang/AppConfig.kt | 1 -
.../java/com/v2ray/ang/dto/EConfigType.kt | 3 +-
.../java/com/v2ray/ang/dto/ProfileItem.kt | 4 +
.../com/v2ray/ang/dto/SubscriptionItem.kt | 1 -
.../com/v2ray/ang/handler/AngConfigManager.kt | 28 +--
.../v2ray/ang/handler/V2RayServiceManager.kt | 3 +-
.../v2ray/ang/handler/V2rayConfigManager.kt | 110 ++++++------
.../java/com/v2ray/ang/ui/MainActivity.kt | 33 ++--
.../com/v2ray/ang/ui/MainRecyclerAdapter.kt | 24 ++-
.../java/com/v2ray/ang/ui/ServerActivity.kt | 3 +-
.../ang/ui/ServerCustomConfigActivity.kt | 2 +-
.../com/v2ray/ang/ui/ServerGroupActivity.kt | 165 ++++++++++++++++++
.../java/com/v2ray/ang/ui/SettingsActivity.kt | 1 -
.../java/com/v2ray/ang/ui/SubEditActivity.kt | 3 -
.../com/v2ray/ang/viewmodel/MainViewModel.kt | 23 ---
.../v2ray/ang/viewmodel/SettingsViewModel.kt | 1 -
.../layout/activity_server_custom_config.xml | 11 +-
.../main/res/layout/activity_server_group.xml | 106 +++++++++++
.../src/main/res/layout/activity_sub_edit.xml | 19 --
V2rayNG/app/src/main/res/menu/menu_main.xml | 8 +-
.../app/src/main/res/values-ar/strings.xml | 11 +-
.../app/src/main/res/values-bn/strings.xml | 11 +-
.../src/main/res/values-bqi-rIR/strings.xml | 11 +-
.../app/src/main/res/values-fa/strings.xml | 11 +-
.../app/src/main/res/values-ru/strings.xml | 11 +-
.../app/src/main/res/values-vi/strings.xml | 11 +-
.../src/main/res/values-zh-rCN/strings.xml | 27 +--
.../src/main/res/values-zh-rTW/strings.xml | 27 +--
V2rayNG/app/src/main/res/values/arrays.xml | 5 -
V2rayNG/app/src/main/res/values/strings.xml | 35 ++--
.../app/src/main/res/xml/pref_settings.xml | 8 -
32 files changed, 473 insertions(+), 248 deletions(-)
create mode 100644 V2rayNG/app/src/main/java/com/v2ray/ang/ui/ServerGroupActivity.kt
create mode 100644 V2rayNG/app/src/main/res/layout/activity_server_group.xml
diff --git a/V2rayNG/app/src/main/AndroidManifest.xml b/V2rayNG/app/src/main/AndroidManifest.xml
index 76890065..f25602a9 100644
--- a/V2rayNG/app/src/main/AndroidManifest.xml
+++ b/V2rayNG/app/src/main/AndroidManifest.xml
@@ -79,6 +79,10 @@
android:name=".ui.ServerCustomConfigActivity"
android:exported="false"
android:windowSoftInputMode="stateUnchanged" />
+
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/AppConfig.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/AppConfig.kt
index 54e780ed..1099e6c2 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/AppConfig.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/AppConfig.kt
@@ -58,7 +58,6 @@ object AppConfig {
const val PREF_DELAY_TEST_URL = "pref_delay_test_url"
const val PREF_LOGLEVEL = "pref_core_loglevel"
const val PREF_OUTBOUND_DOMAIN_RESOLVE_METHOD = "pref_outbound_domain_resolve_method"
- const val PREF_INTELLIGENT_SELECTION_METHOD = "pref_intelligent_selection_method"
const val PREF_MODE = "pref_mode"
const val PREF_IS_BOOTED = "pref_is_booted"
const val PREF_CHECK_UPDATE_PRE_RELEASE = "pref_check_update_pre_release"
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/EConfigType.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/EConfigType.kt
index ed7cad3b..cbc5ee21 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/EConfigType.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/EConfigType.kt
@@ -14,7 +14,8 @@ enum class EConfigType(val value: Int, val protocolScheme: String) {
// TUIC(8, AppConfig.TUIC),
HYSTERIA2(9, AppConfig.HYSTERIA2),
- HTTP(10, AppConfig.HTTP);
+ HTTP(10, AppConfig.HTTP),
+ POLICYGROUP (101, AppConfig.CUSTOM);
companion object {
fun fromInt(value: Int) = entries.firstOrNull { it.value == value }
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/ProfileItem.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/ProfileItem.kt
index 16e4620b..e035f687 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/ProfileItem.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/ProfileItem.kt
@@ -59,6 +59,10 @@ data class ProfileItem(
var bandwidthDown: String? = null,
var bandwidthUp: String? = null,
+ var policyGroupType: String? = null,
+ var policyGroupSubscriptionId: String? = null,
+ var policyGroupFilter: String? = null,
+
) {
companion object {
fun create(configType: EConfigType): ProfileItem {
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/SubscriptionItem.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/SubscriptionItem.kt
index beac5147..8ab871c3 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/SubscriptionItem.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/SubscriptionItem.kt
@@ -11,7 +11,6 @@ data class SubscriptionItem(
var prevProfile: String? = null,
var nextProfile: String? = null,
var filter: String? = null,
- var intelligentSelectionFilter: String? = null,
var allowInsecureUrl: Boolean = false,
var userAgent: String? = null,
)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/AngConfigManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/AngConfigManager.kt
index 779946a4..1108cab5 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/AngConfigManager.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/AngConfigManager.kt
@@ -148,6 +148,7 @@ object AngConfigManager {
EConfigType.TROJAN -> TrojanFmt.toUri(config)
EConfigType.WIREGUARD -> WireguardFmt.toUri(config)
EConfigType.HYSTERIA2 -> Hysteria2Fmt.toUri(config)
+ EConfigType.POLICYGROUP -> ""
}
} catch (e: Exception) {
Log.e(AppConfig.TAG, "Failed to share config for GUID: $guid", e)
@@ -491,31 +492,4 @@ object AngConfigManager {
MmkvManager.encodeSubscription("", subItem)
return 1
}
-
- /**
- * Creates an intelligent selection configuration based on multiple server configurations.
- *
- * @param context The application context used for configuration generation.
- * @param guidList The list of server GUIDs to be included in the intelligent selection.
- * Each GUID represents a server configuration that will be combined.
- * @param subid The subscription ID to associate with the generated configuration.
- * This helps organize the configuration under a specific subscription.
- * @return The GUID key of the newly created intelligent selection configuration,
- * or null if the operation fails (e.g., empty guidList or configuration parsing error).
- */
- fun createIntelligentSelection(
- context: Context,
- guidList: List,
- subid: String
- ): String? {
- if (guidList.isEmpty()) {
- return null
- }
- val result = V2rayConfigManager.genV2rayConfig(context, guidList) ?: return null
- val config = CustomFmt.parse(JsonUtil.toJson(result)) ?: return null
- config.subscriptionId = subid
- val key = MmkvManager.encodeServerConfig("", config)
- MmkvManager.encodeServerRaw(key, JsonUtil.toJsonPretty(result) ?: "")
- return key
- }
}
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2RayServiceManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2RayServiceManager.kt
index edd03898..e6d1f983 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2RayServiceManager.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2RayServiceManager.kt
@@ -5,7 +5,6 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
-import android.os.Build
import android.util.Log
import androidx.core.content.ContextCompat
import com.v2ray.ang.AppConfig
@@ -17,7 +16,6 @@ import com.v2ray.ang.service.ServiceControl
import com.v2ray.ang.service.V2RayProxyOnlyService
import com.v2ray.ang.service.V2RayVpnService
import com.v2ray.ang.util.MessageUtil
-import com.v2ray.ang.handler.PluginServiceManager
import com.v2ray.ang.util.Utils
import go.Seq
import kotlinx.coroutines.CoroutineScope
@@ -100,6 +98,7 @@ object V2RayServiceManager {
val guid = MmkvManager.getSelectServer() ?: return
val config = MmkvManager.decodeServerConfig(guid) ?: return
if (config.configType != EConfigType.CUSTOM
+ && config.configType != EConfigType.POLICYGROUP
&& !Utils.isValidUrl(config.server)
&& !Utils.isPureIpAddress(config.server.orEmpty())
) return
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt
index b87c8dfb..2f3b7030 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt
@@ -44,6 +44,8 @@ object V2rayConfigManager {
val config = MmkvManager.decodeServerConfig(guid) ?: return ConfigResult(false)
return if (config.configType == EConfigType.CUSTOM) {
getV2rayCustomConfig(guid, config)
+ } else if (config.configType == EConfigType.POLICYGROUP) {
+ getV2rayGroupConfig(context, guid, config)
} else {
getV2rayNormalConfig(context, guid, config)
}
@@ -53,27 +55,6 @@ object V2rayConfigManager {
}
}
- /**
- * Generates a V2ray configuration from multiple server profiles.
- *
- * @param context The context of the caller.
- * @param guidList A list of server GUIDs to be included in the generated configuration.
- * Each GUID represents a unique server profile stored in the system.
- * @return A V2rayConfig object containing the combined configuration of all specified servers,
- * or null if the operation fails (e.g., no valid configurations found, parsing errors)
- */
- fun genV2rayConfig(context: Context, guidList: List): V2rayConfig? {
- try {
- val configList = guidList.mapNotNull { guid ->
- MmkvManager.decodeServerConfig(guid)
- }
- return genV2rayMultipleConfig(context, configList)
- } catch (e: Exception) {
- Log.e(AppConfig.TAG, "Failed to generate V2ray config", e)
- return null
- }
- }
-
/**
* Retrieves the speedtest V2ray configuration for the given GUID.
*
@@ -86,6 +67,9 @@ object V2rayConfigManager {
val config = MmkvManager.decodeServerConfig(guid) ?: return ConfigResult(false)
return if (config.configType == EConfigType.CUSTOM) {
getV2rayCustomConfig(guid, config)
+ } else if (config.configType == EConfigType.POLICYGROUP) {
+ // The number of policy groups will not be very large, so no special handling is needed.
+ getV2rayGroupConfig(context, guid, config)
} else {
getV2rayNormalConfig4Speedtest(context, guid, config)
}
@@ -107,6 +91,50 @@ object V2rayConfigManager {
return ConfigResult(true, guid, raw)
}
+ /**
+ * Retrieves the group V2ray configuration.
+ *
+ * @param context The context in which the function is called.
+ * @param guid The unique identifier for the V2ray configuration.
+ * @param config The profile item containing the configuration details.
+ * @return A ConfigResult object containing the result of the configuration retrieval.
+ */
+ private fun getV2rayGroupConfig(context: Context, guid: String, config: ProfileItem): ConfigResult {
+ val result = ConfigResult(false)
+
+ val serverList = MmkvManager.decodeServerList()
+ val configList = serverList
+ .mapNotNull { id -> MmkvManager.decodeServerConfig(id) }
+ .filter { profile ->
+ val subscriptionId = config.policyGroupSubscriptionId
+ if (subscriptionId.isNullOrBlank()) {
+ true
+ } else {
+ profile.subscriptionId == subscriptionId
+ }
+ }
+ .filter { profile ->
+ val filter = config.policyGroupFilter
+ if (filter.isNullOrBlank()) {
+ true
+ } else {
+ try {
+ Regex(filter).containsMatchIn(profile.remarks)
+ } catch (e: Exception) {
+ profile.remarks.contains(filter)
+ }
+ }
+ }
+
+ val v2rayConfig = getV2rayMultipleConfig(context, config, configList) ?: return result
+
+ result.status = true
+ result.content = JsonUtil.toJsonPretty(v2rayConfig) ?: ""
+ result.guid = guid
+
+ return result
+ }
+
/**
* Retrieves the normal V2ray configuration.
*
@@ -164,21 +192,13 @@ object V2rayConfigManager {
return result
}
- private fun genV2rayMultipleConfig(context: Context, configList: List): V2rayConfig? {
+ private fun getV2rayMultipleConfig(context: Context, config: ProfileItem, configList: List): V2rayConfig? {
val validConfigs = configList.asSequence().filter { it.server.isNotNullEmpty() }
.filter { !Utils.isPureIpAddress(it.server!!) || Utils.isValidUrl(it.server!!) }
.filter { it.configType != EConfigType.CUSTOM }
.filter { it.configType != EConfigType.HYSTERIA2 }
- .filter { config ->
- if (config.subscriptionId.isEmpty()) {
- return@filter true
- }
- val subItem = MmkvManager.decodeSubscription(config.subscriptionId)
- if (subItem?.intelligentSelectionFilter.isNullOrEmpty() || config.remarks.isEmpty()) {
- return@filter true
- }
- Regex(pattern = subItem.intelligentSelectionFilter.orEmpty()).containsMatchIn(input = config.remarks)
- }.toList()
+ .filter { it.configType != EConfigType.POLICYGROUP }
+ .toList()
if (validConfigs.isEmpty()) {
Log.w(AppConfig.TAG, "All configs are invalid")
@@ -187,16 +207,7 @@ object V2rayConfigManager {
val v2rayConfig = initV2rayConfig(context) ?: return null
v2rayConfig.log.loglevel = MmkvManager.decodeSettingsString(AppConfig.PREF_LOGLEVEL) ?: "warning"
-
- val subIds = configList.map { it.subscriptionId }.toHashSet()
- val remarks = if (subIds.size == 1 && subIds.first().isNotEmpty()) {
- val sub = MmkvManager.decodeSubscription(subIds.first())
- (sub?.remarks ?: "") + context.getString(R.string.intelligent_selection)
- } else {
- context.getString(R.string.intelligent_selection)
- }
-
- v2rayConfig.remarks = remarks
+ v2rayConfig.remarks = config.remarks
getInbounds(v2rayConfig)
@@ -220,12 +231,12 @@ object V2rayConfigManager {
getDns(v2rayConfig)
- getBalance(v2rayConfig)
+ getBalance(v2rayConfig, config)
- if (MmkvManager.decodeSettingsBool(AppConfig.PREF_LOCAL_DNS_ENABLED) == true) {
+ if (MmkvManager.decodeSettingsBool(AppConfig.PREF_LOCAL_DNS_ENABLED)) {
getCustomLocalDns(v2rayConfig)
}
- if (MmkvManager.decodeSettingsBool(AppConfig.PREF_SPEED_ENABLED) != true) {
+ if (!MmkvManager.decodeSettingsBool(AppConfig.PREF_SPEED_ENABLED)) {
v2rayConfig.stats = null
v2rayConfig.policy = null
}
@@ -469,7 +480,7 @@ object V2rayConfigManager {
)
}
- if (MmkvManager.decodeSettingsBool(AppConfig.PREF_USE_HEV_TUNNEL ,true) == false) {
+ if (MmkvManager.decodeSettingsBool(AppConfig.PREF_USE_HEV_TUNNEL, true) == false) {
// DNS inbound
val remoteDns = SettingsManager.getRemoteDnsServers()
@@ -850,8 +861,7 @@ object V2rayConfigManager {
*
* @param v2rayConfig The V2ray configuration object to be modified with balancing settings
*/
- private fun getBalance(v2rayConfig: V2rayConfig)
- {
+ private fun getBalance(v2rayConfig: V2rayConfig, config: ProfileItem) {
try {
v2rayConfig.routing.rules.forEach { rule ->
if (rule.outboundTag == "proxy") {
@@ -860,7 +870,7 @@ object V2rayConfigManager {
}
}
- if (MmkvManager.decodeSettingsString(AppConfig.PREF_INTELLIGENT_SELECTION_METHOD, "0") == "0") {
+ if (config.policyGroupType == "0") {
val balancer = V2rayConfig.RoutingBean.BalancerBean(
tag = "proxy-round",
selector = listOf("proxy-"),
@@ -1054,6 +1064,7 @@ object V2rayConfigManager {
EConfigType.WIREGUARD -> WireguardFmt.toOutbound(profileItem)
EConfigType.HYSTERIA2 -> null
EConfigType.HTTP -> HttpFmt.toOutbound(profileItem)
+ EConfigType.POLICYGROUP -> null
}
}
@@ -1104,6 +1115,7 @@ object V2rayConfigManager {
)
EConfigType.CUSTOM -> null
+ EConfigType.POLICYGROUP -> null
}
}
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt
index 9f8a2fc5..51d6c96f 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt
@@ -313,6 +313,11 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
true
}
+ R.id.import_manually_policy_group -> {
+ importManually(EConfigType.POLICYGROUP.value)
+ true
+ }
+
R.id.import_manually_vmess -> {
importManually(EConfigType.VMESS.value)
true
@@ -370,14 +375,6 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
true
}
- R.id.intelligent_selection_all -> {
- if (MmkvManager.decodeSettingsString(AppConfig.PREF_OUTBOUND_DOMAIN_RESOLVE_METHOD, "1") != "0") {
- toast(getString(R.string.pre_resolving_domain))
- }
- mainViewModel.createIntelligentSelectionAll()
- true
- }
-
R.id.service_restart -> {
restartV2Ray()
true
@@ -413,12 +410,20 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
}
private fun importManually(createConfigType: Int) {
- startActivity(
- Intent()
- .putExtra("createConfigType", createConfigType)
- .putExtra("subscriptionId", mainViewModel.subscriptionId)
- .setClass(this, ServerActivity::class.java)
- )
+ if (createConfigType == EConfigType.POLICYGROUP.value) {
+ startActivity(
+ Intent()
+ .putExtra("subscriptionId", mainViewModel.subscriptionId)
+ .setClass(this, ServerGroupActivity::class.java)
+ )
+ } else {
+ startActivity(
+ Intent()
+ .putExtra("createConfigType", createConfigType)
+ .putExtra("subscriptionId", mainViewModel.subscriptionId)
+ .setClass(this, ServerActivity::class.java)
+ )
+ }
}
/**
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainRecyclerAdapter.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainRecyclerAdapter.kt
index 7f27fd6f..43f32af7 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainRecyclerAdapter.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainRecyclerAdapter.kt
@@ -57,7 +57,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter setContentView(R.layout.activity_server_vmess)
EConfigType.CUSTOM -> return
@@ -147,6 +147,7 @@ class ServerActivity : BaseActivity() {
EConfigType.TROJAN -> setContentView(R.layout.activity_server_trojan)
EConfigType.WIREGUARD -> setContentView(R.layout.activity_server_wireguard)
EConfigType.HYSTERIA2 -> setContentView(R.layout.activity_server_hysteria2)
+ EConfigType.POLICYGROUP -> return
}
sp_network?.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ServerCustomConfigActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ServerCustomConfigActivity.kt
index 44e0b32b..ea0d3656 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ServerCustomConfigActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ServerCustomConfigActivity.kt
@@ -32,7 +32,7 @@ class ServerCustomConfigActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
- title = getString(R.string.title_server)
+ title = EConfigType.CUSTOM.toString()
if (!Utils.getDarkModeStatus(this)) {
binding.editor.colorScheme = EditorTheme.INTELLIJ_LIGHT
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ServerGroupActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ServerGroupActivity.kt
new file mode 100644
index 00000000..9ca96b28
--- /dev/null
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ServerGroupActivity.kt
@@ -0,0 +1,165 @@
+package com.v2ray.ang.ui
+
+import android.os.Bundle
+import android.text.TextUtils
+import android.view.Menu
+import android.view.MenuItem
+import androidx.appcompat.app.AlertDialog
+import com.v2ray.ang.R
+import com.v2ray.ang.databinding.ActivityServerGroupBinding
+import com.v2ray.ang.dto.EConfigType
+import com.v2ray.ang.dto.ProfileItem
+import com.v2ray.ang.extension.toast
+import com.v2ray.ang.extension.toastSuccess
+import android.widget.ArrayAdapter
+import com.v2ray.ang.handler.MmkvManager
+import com.v2ray.ang.util.Utils
+
+class ServerGroupActivity : BaseActivity() {
+ private val binding by lazy { ActivityServerGroupBinding.inflate(layoutInflater) }
+
+ private val editGuid by lazy { intent.getStringExtra("guid").orEmpty() }
+ private val isRunning by lazy {
+ intent.getBooleanExtra("isRunning", false)
+ && editGuid.isNotEmpty()
+ && editGuid == MmkvManager.getSelectServer()
+ }
+ private val subscriptionId by lazy {
+ intent.getStringExtra("subscriptionId")
+ }
+ private val subIds = mutableListOf()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(binding.root)
+ title = EConfigType.POLICYGROUP.toString()
+
+ val config = MmkvManager.decodeServerConfig(editGuid)
+ populateSubscriptionSpinner()
+
+ if (config != null) {
+ bindingServer(config)
+ } else {
+ clearServer()
+ }
+ }
+
+ /**
+ * Binding selected server config
+ */
+ private fun bindingServer(config: ProfileItem): Boolean {
+ binding.etRemarks.text = Utils.getEditable(config.remarks)
+ binding.etPolicyGroupFilter.text = Utils.getEditable(config.policyGroupFilter)
+
+ val type = config.policyGroupType?.toInt() ?: 0
+ binding.spPolicyGroupType.setSelection(type)
+
+ val pos = subIds.indexOf(config.policyGroupSubscriptionId ?: "").let { if (it >= 0) it else 0 }
+ binding.spPolicyGroupSubId.setSelection(pos)
+
+ return true
+ }
+
+ /**
+ * clear or init server config
+ */
+ private fun clearServer(): Boolean {
+ binding.etRemarks.text = null
+ binding.etPolicyGroupFilter.text = null
+ return true
+ }
+
+ /**
+ * save server config
+ */
+ private fun saveServer(): Boolean {
+ if (TextUtils.isEmpty(binding.etRemarks.text.toString())) {
+ toast(R.string.server_lab_remarks)
+ return false
+ }
+
+ val config = MmkvManager.decodeServerConfig(editGuid) ?: ProfileItem.create(EConfigType.POLICYGROUP)
+ config.remarks = binding.etRemarks.text.toString().trim()
+ config.policyGroupFilter = binding.etPolicyGroupFilter.text.toString().trim()
+
+ config.policyGroupType = binding.spPolicyGroupType.selectedItemPosition.toString()
+
+ val selPos = binding.spPolicyGroupSubId.selectedItemPosition
+ config.policyGroupSubscriptionId = if (selPos >= 0 && selPos < subIds.size) subIds[selPos] else null
+
+ if (config.subscriptionId.isEmpty() && !subscriptionId.isNullOrEmpty()) {
+ config.subscriptionId = subscriptionId.orEmpty()
+ }
+ MmkvManager.encodeServerConfig(editGuid, config)
+ toastSuccess(R.string.toast_success)
+ finish()
+ return true
+ }
+
+ /**
+ * save server config
+ */
+ private fun deleteServer(): Boolean {
+ if (editGuid.isNotEmpty()) {
+ AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm)
+ .setPositiveButton(android.R.string.ok) { _, _ ->
+ MmkvManager.removeServer(editGuid)
+ finish()
+ }
+ .setNegativeButton(android.R.string.cancel) { _, _ ->
+ // do nothing
+ }
+ .show()
+ }
+ return true
+ }
+
+ private fun populateSubscriptionSpinner() {
+ val subs = MmkvManager.decodeSubscriptions()
+ val displayList = mutableListOf(getString(R.string.filter_config_all)) //none
+ subIds.clear()
+ subIds.add("") // index 0 => All
+ subs.forEach { (id, item) ->
+ val name = when {
+ item.remarks.isNotBlank() -> item.remarks
+ else -> id
+ }
+ displayList.add(name)
+ subIds.add(id)
+ }
+ val subAdapter = ArrayAdapter(this, android.R.layout.simple_spinner_item, displayList)
+ subAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
+ binding.spPolicyGroupSubId.adapter = subAdapter
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu): Boolean {
+ menuInflater.inflate(R.menu.action_server, menu)
+ val delButton = menu.findItem(R.id.del_config)
+ val saveButton = menu.findItem(R.id.save_config)
+
+ if (editGuid.isNotEmpty()) {
+ if (isRunning) {
+ delButton?.isVisible = false
+ saveButton?.isVisible = false
+ }
+ } else {
+ delButton?.isVisible = false
+ }
+
+ return super.onCreateOptionsMenu(menu)
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
+ R.id.del_config -> {
+ deleteServer()
+ true
+ }
+
+ R.id.save_config -> {
+ saveServer()
+ true
+ }
+
+ else -> super.onOptionsItemSelected(item)
+ }
+}
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/SettingsActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/SettingsActivity.kt
index 9b6107ca..385e5632 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/SettingsActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/SettingsActivity.kt
@@ -285,7 +285,6 @@ class SettingsActivity : BaseActivity() {
AppConfig.PREF_UI_MODE_NIGHT,
AppConfig.PREF_LOGLEVEL,
AppConfig.PREF_OUTBOUND_DOMAIN_RESOLVE_METHOD,
- AppConfig.PREF_INTELLIGENT_SELECTION_METHOD,
AppConfig.PREF_MODE,
AppConfig.PREF_HEV_TUNNEL_LOGLEVEL
).forEach { key ->
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 022e95ba..f342628d 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
@@ -46,7 +46,6 @@ class SubEditActivity : BaseActivity() {
binding.etUrl.text = Utils.getEditable(subItem.url)
binding.etUserAgent.text = Utils.getEditable(subItem.userAgent)
binding.etFilter.text = Utils.getEditable(subItem.filter)
- binding.etIntelligentSelectionFilter.text = Utils.getEditable(subItem.intelligentSelectionFilter)
binding.chkEnable.isChecked = subItem.enabled
binding.autoUpdateCheck.isChecked = subItem.autoUpdate
binding.allowInsecureUrl.isChecked = subItem.allowInsecureUrl
@@ -62,7 +61,6 @@ class SubEditActivity : BaseActivity() {
binding.etRemarks.text = null
binding.etUrl.text = null
binding.etFilter.text = null
- binding.etIntelligentSelectionFilter.text = null
binding.chkEnable.isChecked = true
binding.etPreProfile.text = null
binding.etNextProfile.text = null
@@ -79,7 +77,6 @@ class SubEditActivity : BaseActivity() {
subItem.url = binding.etUrl.text.toString()
subItem.userAgent = binding.etUserAgent.text.toString()
subItem.filter = binding.etFilter.text.toString()
- subItem.intelligentSelectionFilter = binding.etIntelligentSelectionFilter.text.toString()
subItem.enabled = binding.chkEnable.isChecked
subItem.autoUpdate = binding.autoUpdateCheck.isChecked
subItem.prevProfile = binding.etPreProfile.text.toString()
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 f9323c98..ec5cb7ee 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
@@ -384,29 +384,6 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
MmkvManager.encodeServerList(serverList)
}
- /**
- * Creates an intelligent selection configuration containing all currently filtered servers.
- */
- fun createIntelligentSelectionAll() {
- viewModelScope.launch(Dispatchers.IO) {
- val key = AngConfigManager.createIntelligentSelection(
- getApplication(),
- serversCache.map { it.guid }.toList(),
- subscriptionId
- )
-
- launch(Dispatchers.Main) {
- if (key.isNullOrEmpty()) {
- getApplication().toastError(R.string.toast_failure)
- } else {
- getApplication().toastSuccess(R.string.toast_success)
- MmkvManager.setSelectServer(key)
- reloadServerList()
- }
- }
- }
- }
-
/**
* Initializes assets.
* @param assets The asset manager.
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/SettingsViewModel.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/SettingsViewModel.kt
index 5e211e96..441afcb2 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/SettingsViewModel.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/SettingsViewModel.kt
@@ -51,7 +51,6 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
AppConfig.PREF_SOCKS_PORT,
AppConfig.PREF_LOGLEVEL,
AppConfig.PREF_OUTBOUND_DOMAIN_RESOLVE_METHOD,
- AppConfig.PREF_INTELLIGENT_SELECTION_METHOD,
AppConfig.PREF_LANGUAGE,
AppConfig.PREF_UI_MODE_NIGHT,
AppConfig.PREF_ROUTING_DOMAIN_STRATEGY,
diff --git a/V2rayNG/app/src/main/res/layout/activity_server_custom_config.xml b/V2rayNG/app/src/main/res/layout/activity_server_custom_config.xml
index d6d587b8..5dabff6b 100644
--- a/V2rayNG/app/src/main/res/layout/activity_server_custom_config.xml
+++ b/V2rayNG/app/src/main/res/layout/activity_server_custom_config.xml
@@ -9,12 +9,12 @@
+ android:orientation="vertical"
+ android:padding="@dimen/padding_spacing_dp16">
@@ -73,7 +73,6 @@
android:id="@+id/editor"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_marginTop="@dimen/padding_spacing_dp16"
android:gravity="top|start" />
diff --git a/V2rayNG/app/src/main/res/layout/activity_server_group.xml b/V2rayNG/app/src/main/res/layout/activity_server_group.xml
new file mode 100644
index 00000000..74e861bb
--- /dev/null
+++ b/V2rayNG/app/src/main/res/layout/activity_server_group.xml
@@ -0,0 +1,106 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/V2rayNG/app/src/main/res/layout/activity_sub_edit.xml b/V2rayNG/app/src/main/res/layout/activity_sub_edit.xml
index c3db6a96..87a44c58 100644
--- a/V2rayNG/app/src/main/res/layout/activity_sub_edit.xml
+++ b/V2rayNG/app/src/main/res/layout/activity_sub_edit.xml
@@ -112,25 +112,6 @@
-
-
-
-
-
-
-
-
+
-
- استيراد التكوين من رمز الاستجابة السريعة (QRcode)
استيراد التكوين من الحافظة
Import config from locally
+ Add [Policy group]
الكتابة يدويًا [VMess]
الكتابة يدويًا [VLESS]
الكتابة يدويًا [Shadowsocks]
@@ -278,7 +279,6 @@
تحديث الاشتراك (أول خطوة)
Tcping لجميع الإعدادات
اختبر جميع الإعدادات (3)
- Creating Intelligent Selection Current Group Configuration
Asset files
الفرز حسب نتائج الاختبار (5)
تصفية ملف التكوين
@@ -332,6 +332,10 @@
Check Pre-release
Checking for update…
+ Policy group type
+ From subscription group
+ Remarks regular filter
+
- رمز استجابة سريعة (QRcode)
- تصدير إلى الحافظة
@@ -373,10 +377,7 @@
- Resolve and add to DNS Hosts
- Resolve and replace domain
- Intelligent Selection
- Remarks Intelligent Selection regular filter
- Intelligent Selection Method
-
+
- Least Ping
- Least Load
diff --git a/V2rayNG/app/src/main/res/values-bn/strings.xml b/V2rayNG/app/src/main/res/values-bn/strings.xml
index 3b74dc02..7fc1692c 100644
--- a/V2rayNG/app/src/main/res/values-bn/strings.xml
+++ b/V2rayNG/app/src/main/res/values-bn/strings.xml
@@ -29,6 +29,7 @@
QR কোড থেকে কনফিগারেশন আমদানি করুন
ক্লিপবোর্ড থেকে কনফিগারেশন আমদানি করুন
Import config from locally
+ Add [Policy group]
ম্যানুয়ালি টাইপ করুন [VMess]
ম্যানুয়ালি টাইপ করুন [VLESS]
ম্যানুয়ালি টাইপ করুন [Shadowsocks]
@@ -278,7 +279,6 @@
সাবস্ক্রিপশন আপডেট
সব কনফিগারেশন TCPing
সব কনফিগারেশন প্রকৃত বিলম্ব
- Creating Intelligent Selection Current Group Configuration
Asset files
টেস্ট ফলাফল দ্বারা সাজানো
কনফিগারেশন ফাইল ফিল্টার করুন
@@ -331,6 +331,10 @@
Check Pre-release
Checking for update…
+ Policy group type
+ From subscription group
+ Remarks regular filter
+
- QR কোড
- ক্লিপবোর্ডে রপ্তানি করুন
@@ -378,10 +382,7 @@
- Resolve and add to DNS Hosts
- Resolve and replace domain
- Intelligent Selection
- Remarks Intelligent Selection regular filter
- Intelligent Selection Method
-
+
- Least Ping
- Least Load
diff --git a/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml b/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml
index 32651a18..fc922c32 100644
--- a/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml
+++ b/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml
@@ -29,6 +29,7 @@
و من ٱووردن کانفیگ ز QRcode
و من ٱووردن کانفیگ ز کلیپ بورد
و من ٱووردن کانفیگ ز مهلی
+ Add [Policy group]
هؽل دستی[VMess]
هؽل دستی[VLESS]
هؽل دستی[Shadowsocks]
@@ -278,7 +279,6 @@
ورۊ کردن اشتراک بونکۊ سکویی
Tcping کانفیگا بونکۊ سکویی
تئخیر واقعی کانفیگا بونکۊ سکویی
- وورکل پسند هۊشمند کانفیگ بونکۊ سکویی
فایلا بونچک جوقرافیایی
ترتیب و ری نتیجه یل آزمایش
فیلتر کردن کانفیگا
@@ -340,6 +340,10 @@
واجۊری نوسخه یل پؽش ز تیجنیڌن
ورۊ رسۊوی ن هونی واجۊری اکونه...
+ Policy group type
+ From subscription group
+ Remarks regular filter
+
- QRcode
- و در کشیڌن من کلیپ بورد
@@ -388,10 +392,7 @@
- هل وو ٱووردن و میزبووݩ یل دامنه DNS
- هل وو جایونی دامنه
- پسند هۊشمند
- گوڌنا دیاری پسند هۊشمند فیلتر مئمۊلی
- بارت پسند هۊشمند
-
+
- کم ترین پینگ
- کم ترین بار(لود)
diff --git a/V2rayNG/app/src/main/res/values-fa/strings.xml b/V2rayNG/app/src/main/res/values-fa/strings.xml
index 69f54a16..27ecd931 100644
--- a/V2rayNG/app/src/main/res/values-fa/strings.xml
+++ b/V2rayNG/app/src/main/res/values-fa/strings.xml
@@ -29,6 +29,7 @@
کانفیگ را از QRcode وارد کنید
کانفیگ را از کلیپ بورد وارد کنید
کانفیگ را از محلی وارد کنید
+ Add [Policy group]
تایپ دستی[VMESS]
تایپ دستی[VLESS]
تایپ دستی[SHADOWSOCKS]
@@ -275,7 +276,6 @@
بهروزرسانی گروه فعلی اشتراک
TCPING کانفیگ های گروه فعلی
تاخیر واقعی کانفیگ های گروه فعلی
- ایجاد کانفیگ انتخاب هوشمند برای گروه فعلی
فایل های منبع جغرافیایی
مرتب سازی بر اساس نتایج آزمایش
فیلتر کردن کانفیگها
@@ -337,6 +337,10 @@
بررسی نسخه پیش از انتشار
در حال بررسی برای بهروزرسانی…
+ Policy group type
+ From subscription group
+ Remarks regular filter
+
- QRcode
- خروجی گرفتن در کلیپ بورد
@@ -387,10 +391,7 @@
- Resolve and add to DNS Hosts
- Resolve and replace domain
- انتخاب هوشمند
- فیلتر نام مستعار برای انتخاب هوشمند
- روش انتخاب هوشمند
-
+
- کمترین پینگ
- کمترین بار(لود)
diff --git a/V2rayNG/app/src/main/res/values-ru/strings.xml b/V2rayNG/app/src/main/res/values-ru/strings.xml
index 1c2ac143..84555bd8 100644
--- a/V2rayNG/app/src/main/res/values-ru/strings.xml
+++ b/V2rayNG/app/src/main/res/values-ru/strings.xml
@@ -29,6 +29,7 @@
Импорт из QR-кода
Импорт из буфера обмена
Импорт из файла
+ Add [Policy group]
Ручной ввод VMess
Ручной ввод VLESS
Ручной ввод Shadowsocks
@@ -277,7 +278,6 @@
Обновить подписку группы
Проверить профили группы
Проверить задержку профилей группы
- Создать конфигурацию умного выбора
Файлы ресурсов
Сортировать по результатам теста
Фильтр профилей
@@ -339,6 +339,10 @@
Искать предварительный выпуск
Проверка обновления…
+ Policy group type
+ From subscription group
+ Remarks regular filter
+
- QR-код
- Экспорт в буфер обмена
@@ -387,10 +391,7 @@
- Определять и добавлять к узлам DNS
- Определять и заменять домен
- Умный выбор
- Название фильтра умного выбора
- Принцип умного выбора
-
+
- Наименьшая задержка
- Наименьшая нагрузка
diff --git a/V2rayNG/app/src/main/res/values-vi/strings.xml b/V2rayNG/app/src/main/res/values-vi/strings.xml
index 749b9516..f377976f 100644
--- a/V2rayNG/app/src/main/res/values-vi/strings.xml
+++ b/V2rayNG/app/src/main/res/values-vi/strings.xml
@@ -29,6 +29,7 @@
Nhập cấu hình từ mã QR
Nhập cấu hình từ Clipboard
Import config from locally
+ Add [Policy group]
Nhập thủ công [VMess]
Nhập thủ công [VLESS]
Nhập thủ công [ShadowSocks]
@@ -278,7 +279,6 @@
Cập nhật các gói đăng ký
Ping tất cả máy chủ
Kiểm tra HTTP tất cả máy chủ
- Creating Intelligent Selection Current Group Configuration
Asset files
Sắp xếp lại theo lần kiểm tra cuối cùng
Lọc cấu hình theo các gói đăng ký
@@ -333,6 +333,10 @@
Check Pre-release
Checking for update…
+ Policy group type
+ From subscription group
+ Remarks regular filter
+
- Xuất ra mã QR (Chụp màn hình để lưu)
- Sao chép vào Clipboard
@@ -375,10 +379,7 @@
- Resolve and add to DNS Hosts
- Resolve and replace domain
- Intelligent Selection
- Remarks Intelligent Selection regular filter
- Intelligent Selection Method
-
+
- Least Ping
- Least Load
diff --git a/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml b/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml
index d109cbfc..2fb96a25 100644
--- a/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml
+++ b/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml
@@ -29,14 +29,15 @@
扫描二维码
从剪贴板导入
从本地导入
- 手动输入 [VMess]
- 手动输入 [VLESS]
- 手动输入 [Shadowsocks]
- 手动输入 [SOCKS]
- 手动输入 [HTTP]
- 手动输入 [Trojan]
- 手动输入 [Wireguard]
- 手动输入 [Hysteria2]
+ 添加 [策略组]
+ 添加 [VMess]
+ 添加 [VLESS]
+ 添加 [Shadowsocks]
+ 添加 [SOCKS]
+ 添加 [HTTP]
+ 添加 [Trojan]
+ 添加 [Wireguard]
+ 添加 [Hysteria2]
确认删除?
删除前请先测试!确认删除?
别名 (remarks)
@@ -275,7 +276,6 @@
更新当前组订阅
测试当前组配置 Tcping
测试当前组配置真连接
- 生成当前组智能选择配置
资源文件
按测试结果排序
过滤配置
@@ -331,6 +331,10 @@
检查 Pre-release
正在检查更新中…
+ 策略组类型
+ 来自订阅分组
+ 别名正则过滤
+
- 二维码
- 导出至剪贴板
@@ -379,10 +383,7 @@
- 解析后添加至 DNS Hosts
- 解析后替换原域名
- 智能选择
- 别名智能选择正则过滤
- 智能选择方式
-
+
- 最低延迟
- 最稳定
diff --git a/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml b/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml
index e53cb7b4..aa14cc09 100644
--- a/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml
+++ b/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml
@@ -29,14 +29,15 @@
從 QR Code 匯入設定
從剪貼簿匯入設定
從本地匯入
- 手動鍵入 [VMess]
- 手動鍵入 [VLESS]
- 手動鍵入 [Shadowsocks]
- 手動鍵入 [SOCKS]
- 手動鍵入 [HTTP]
- 手動鍵入 [Trojan]
- 手動鍵入 [Wireguard]
- 手動鍵入 [Hysteria2]
+ 新增 [策略組]
+ 新增 [VMess]
+ 新增 [VLESS]
+ 新增 [Shadowsocks]
+ 新增 [SOCKS]
+ 新增 [HTTP]
+ 新增 [Trojan]
+ 新增 [Wireguard]
+ 新增 [Hysteria2]
確定刪除?
刪除前請先測試!確認刪除?
備註
@@ -276,7 +277,6 @@
更新目前群組訂閱
偵測目前群組設定 Tcping
偵測目前群組設定真延遲
- 創建目前群組智慧選擇
資源檔案
依偵測結果排序
過濾設定
@@ -331,6 +331,10 @@
檢查 Pre-release
正在檢查更新中…
+ 策略群組類型
+ 來自訂閱分組
+ 別名正規過濾
+
- 二維碼
- 匯出至剪貼簿
@@ -379,10 +383,7 @@
- 解析後加入 DNS Hosts
- 解析後替換原網域名稱
- 智慧選擇
- 別名智慧選擇正則過濾
- 智慧選擇方案
-
+
- 最低延遲
- 最少卡頓
diff --git a/V2rayNG/app/src/main/res/values/arrays.xml b/V2rayNG/app/src/main/res/values/arrays.xml
index e8ef2476..1cdaede4 100644
--- a/V2rayNG/app/src/main/res/values/arrays.xml
+++ b/V2rayNG/app/src/main/res/values/arrays.xml
@@ -215,9 +215,4 @@
- 2
-
- - 0
- - 1
-
-
\ No newline at end of file
diff --git a/V2rayNG/app/src/main/res/values/strings.xml b/V2rayNG/app/src/main/res/values/strings.xml
index b87b4bd3..e821aecc 100644
--- a/V2rayNG/app/src/main/res/values/strings.xml
+++ b/V2rayNG/app/src/main/res/values/strings.xml
@@ -27,17 +27,18 @@
Save configuration
Edit configuration
Delete configuration
- Import configuration from QRcode
- Import configuration from Clipboard
- Import configuration from locally
- Type manually[VMess]
- Type manually[VLESS]
- Type manually[Shadowsocks]
- Type manually[SOCKS]
- Type manually[HTTP]
- Type manually[Trojan]
- Type manually[Wireguard]
- Type manually[Hysteria2]
+ Import from QRcode
+ Import from Clipboard
+ Import from locally
+ Add [Policy group]
+ Add [VMess]
+ Add [VLESS]
+ Add [Shadowsocks]
+ Add [SOCKS]
+ Add [HTTP]
+ Add [Trojan]
+ Add [Wireguard]
+ Add [Hysteria2]
Confirm delete ?
Please test before deleting! Confirm delete ?
remarks
@@ -281,7 +282,6 @@
Update current group subscription
Tcping current group configuration
Real delay current group configuration
- Creating Intelligent Selection Current Group Configuration
Asset files
Sorting by test results
Filter configuration
@@ -343,6 +343,10 @@
Check Pre-release
Checking for update…
+ Policy group type
+ From subscription group
+ Remarks regular filter
+
- QRcode
- Export to clipboard
@@ -391,13 +395,12 @@
- Resolve and add to DNS Hosts
- Resolve and replace domain
- Intelligent Selection
- Remarks Intelligent Selection regular filter
- Intelligent Selection Method
-
+
+
- Least Ping
- Least Load
+
Pre-resolving domain…
diff --git a/V2rayNG/app/src/main/res/xml/pref_settings.xml b/V2rayNG/app/src/main/res/xml/pref_settings.xml
index 029af7b5..7267234a 100644
--- a/V2rayNG/app/src/main/res/xml/pref_settings.xml
+++ b/V2rayNG/app/src/main/res/xml/pref_settings.xml
@@ -256,14 +256,6 @@
android:summary="%s"
android:title="@string/title_outbound_domain_resolve_method" />
-
-