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" /> - -