Compare commits

..

10 Commits

Author SHA1 Message Date
2dust
c78ef380cc up 1.10.10 2025-07-24 19:34:31 +08:00
2dust
57362a4bde Update AndroidLibXrayLite 2025-07-24 19:28:19 +08:00
DHR60
7f24ad534f Fix Intelligent Selection not working (#4767) 2025-07-24 19:22:15 +08:00
2dust
680832614b up 1.10.9 2025-07-10 20:20:53 +08:00
2dust
4357abbff4 Bug fix
https://github.com/2dust/v2rayNG/issues/4723
2025-07-10 20:11:34 +08:00
solokot
905be66c3f Update Russian translation (#4725) 2025-07-09 19:44:45 +08:00
Hossein Abaspanah
318a7b54a5 Update Luri Bakhtiari translation (#4724) 2025-07-09 19:44:34 +08:00
DHR60
5db2df77a0 feat. Intelligent Selection (#4716)
rename

Adds intelligent selection method setting

Adds KDoc
2025-07-07 20:17:09 +08:00
DHR60
d039cb9edf Fix export count (#4713) 2025-07-06 11:16:32 +08:00
solokot
9a1654bae9 Update Russian translation (#4701) 2025-07-01 15:23:05 +08:00
27 changed files with 461 additions and 49 deletions

View File

@@ -12,8 +12,8 @@ android {
applicationId = "com.v2ray.ang"
minSdk = 21
targetSdk = 35
versionCode = 658
versionName = "1.10.8"
versionCode = 660
versionName = "1.10.10"
multiDexEnabled = true
val abiFilterList = (properties["ABI_FILTERS"] as? String)?.split(';')

View File

@@ -57,6 +57,7 @@ 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"

View File

@@ -11,6 +11,7 @@ data class SubscriptionItem(
var prevProfile: String? = null,
var nextProfile: String? = null,
var filter: String? = null,
var intelligentSelectionFilter: String? = null,
var allowInsecureUrl: Boolean = false,
)

View File

@@ -496,14 +496,14 @@ data class V2rayConfig(
var domainStrategy: String,
var domainMatcher: String? = null,
var rules: ArrayList<RulesBean>,
val balancers: List<Any>? = null
var balancers: List<BalancerBean>? = null
) {
data class RulesBean(
var type: String = "field",
var ip: ArrayList<String>? = null,
var domain: ArrayList<String>? = null,
var outboundTag: String = "",
var outboundTag: String? = null,
var balancerTag: String? = null,
var port: String? = null,
val sourcePort: String? = null,
@@ -515,6 +515,32 @@ data class V2rayConfig(
val attrs: String? = null,
val domainMatcher: String? = null
)
data class BalancerBean(
val tag: String,
val selector: List<String>,
val fallbackTag: String? = null,
val strategy: StrategyObject? = null
)
data class StrategyObject(
val type: String = "random", // "random" | "roundRobin" | "leastPing" | "leastLoad"
val settings: StrategySettingsObject? = null
)
data class StrategySettingsObject(
val expected: Int? = null,
val maxRTT: String? = null,
val tolerance: Double? = null,
val baselines: List<String>? = null,
val costs: List<CostObject>? = null
)
data class CostObject(
val regexp: Boolean = false,
val match: String,
val value: Double
)
}
data class PolicyBean(
@@ -532,6 +558,26 @@ data class V2rayConfig(
)
}
data class ObservatoryObject(
val subjectSelector: List<String>,
val probeUrl: String,
val probeInterval: String,
val enableConcurrency: Boolean = false
)
data class BurstObservatoryObject(
val subjectSelector: List<String>,
val pingConfig: PingConfigObject
) {
data class PingConfigObject(
val destination: String,
val connectivity: String? = null,
val interval: String,
val sampling: Int,
val timeout: String? = null
)
}
data class FakednsBean(
var ipPool: String = "198.18.0.0/15",
var poolSize: Int = 10000

View File

@@ -71,7 +71,7 @@ object AngConfigManager {
if (sb.count() > 0) {
Utils.setClipboard(context, sb.toString())
}
return sb.lines().count()
return sb.lines().count() - 1
} catch (e: Exception) {
Log.e(AppConfig.TAG, "Failed to share non-custom configs to clipboard", e)
return -1
@@ -490,4 +490,31 @@ 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<String>,
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
}
}

View File

@@ -4,6 +4,7 @@ import android.content.Context
import android.text.TextUtils
import android.util.Log
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
import com.v2ray.ang.dto.ConfigResult
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.dto.NetworkType
@@ -15,6 +16,7 @@ import com.v2ray.ang.dto.V2rayConfig.OutboundBean.OutSettingsBean
import com.v2ray.ang.dto.V2rayConfig.OutboundBean.StreamSettingsBean
import com.v2ray.ang.dto.V2rayConfig.RoutingBean.RulesBean
import com.v2ray.ang.extension.isNotNullEmpty
import com.v2ray.ang.fmt.CustomFmt
import com.v2ray.ang.fmt.HttpFmt
import com.v2ray.ang.fmt.ShadowsocksFmt
import com.v2ray.ang.fmt.SocksFmt
@@ -52,6 +54,27 @@ 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<String>): 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.
*
@@ -142,6 +165,80 @@ object V2rayConfigManager {
return result
}
private fun genV2rayMultipleConfig(context: Context, configList: List<ProfileItem>): 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!!).containsMatchIn(input = config.remarks)
}.toList()
if (validConfigs.isEmpty()) {
Log.w(AppConfig.TAG, "All configs are invalid")
return null
}
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
getInbounds(v2rayConfig)
v2rayConfig.outbounds.removeAt(0)
val outboundsList = mutableListOf<V2rayConfig.OutboundBean>()
var index = 0
for (config in validConfigs) {
index++
val outbound = convertProfile2Outbound(config) ?: continue
val ret = updateOutboundWithGlobalSettings(outbound)
if (!ret) continue
outbound.tag = "proxy-$index"
outboundsList.add(outbound)
}
outboundsList.addAll(v2rayConfig.outbounds)
v2rayConfig.outbounds = ArrayList(outboundsList)
getRouting(v2rayConfig)
getFakeDns(v2rayConfig)
getDns(v2rayConfig)
getBalance(v2rayConfig)
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_LOCAL_DNS_ENABLED) == true) {
getCustomLocalDns(v2rayConfig)
}
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_SPEED_ENABLED) != true) {
v2rayConfig.stats = null
v2rayConfig.policy = null
}
//Resolve and add to DNS Hosts
if (MmkvManager.decodeSettingsString(AppConfig.PREF_OUTBOUND_DOMAIN_RESOLVE_METHOD, "1") == "1") {
resolveOutboundDomainsToHosts(v2rayConfig)
}
return v2rayConfig
}
/**
* Retrieves the normal V2ray configuration for speedtest.
*
@@ -741,6 +838,78 @@ object V2rayConfigManager {
return true
}
/**
* Configures load balancing settings for the V2ray configuration.
*
* @param v2rayConfig The V2ray configuration object to be modified with balancing settings
*/
private fun getBalance(v2rayConfig: V2rayConfig)
{
try {
v2rayConfig.routing.rules.forEach { rule ->
if (rule.outboundTag == "proxy") {
rule.outboundTag = null
rule.balancerTag = "proxy-round"
}
}
if (MmkvManager.decodeSettingsString(AppConfig.PREF_INTELLIGENT_SELECTION_METHOD, "0") == "0") {
val balancer = V2rayConfig.RoutingBean.BalancerBean(
tag = "proxy-round",
selector = listOf("proxy-"),
strategy = V2rayConfig.RoutingBean.StrategyObject(
type = "leastPing"
)
)
v2rayConfig.routing.balancers = listOf(balancer)
v2rayConfig.observatory = V2rayConfig.ObservatoryObject(
subjectSelector = listOf("proxy-"),
probeUrl = MmkvManager.decodeSettingsString(AppConfig.PREF_DELAY_TEST_URL) ?: AppConfig.DELAY_TEST_URL,
probeInterval = "3m",
enableConcurrency = true
)
} else {
val balancer = V2rayConfig.RoutingBean.BalancerBean(
tag = "proxy-round",
selector = listOf("proxy-"),
strategy = V2rayConfig.RoutingBean.StrategyObject(
type = "leastLoad"
)
)
v2rayConfig.routing.balancers = listOf(balancer)
v2rayConfig.burstObservatory = V2rayConfig.BurstObservatoryObject(
subjectSelector = listOf("proxy-"),
pingConfig = V2rayConfig.BurstObservatoryObject.PingConfigObject(
destination = MmkvManager.decodeSettingsString(AppConfig.PREF_DELAY_TEST_URL) ?: AppConfig.DELAY_TEST_URL,
interval = "5m",
sampling = 2,
timeout = "30s"
)
)
}
if (v2rayConfig.routing.domainStrategy == "IPIfNonMatch") {
v2rayConfig.routing.rules.add(
RulesBean(
ip = arrayListOf("0.0.0.0/0", "::/0"),
balancerTag = "proxy-round",
type = "field"
)
)
} else {
v2rayConfig.routing.rules.add(
RulesBean(
network = "tcp,udp",
balancerTag = "proxy-round",
type = "field"
)
)
}
} catch (e: Exception) {
Log.e(AppConfig.TAG, "Failed to configure balance", e)
}
}
/**
* Updates the outbound with fragment settings for traffic optimization.
*

View File

@@ -194,25 +194,7 @@ class V2RayVpnService : VpnService(), ServiceControl {
builder.setSession(V2RayServiceManager.getRunningServerName())
val selfPackageName = BuildConfig.APPLICATION_ID
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_PER_APP_PROXY)) {
val apps = MmkvManager.decodeSettingsStringSet(AppConfig.PREF_PER_APP_PROXY_SET)
val bypassApps = MmkvManager.decodeSettingsBool(AppConfig.PREF_BYPASS_APPS)
//process self package
if (bypassApps) apps?.add(selfPackageName) else apps?.remove(selfPackageName)
apps?.forEach {
try {
if (bypassApps)
builder.addDisallowedApplication(it)
else
builder.addAllowedApplication(it)
} catch (e: PackageManager.NameNotFoundException) {
Log.e(AppConfig.TAG, "Failed to configure app in VPN: ${e.localizedMessage}", e)
}
}
} else {
builder.addDisallowedApplication(selfPackageName)
}
configurePerAppProxy(builder)
// Close the old interface since the parameters have been changed.
try {
@@ -249,6 +231,51 @@ class V2RayVpnService : VpnService(), ServiceControl {
return false
}
/**
* Configures per-app proxy rules for the VPN builder.
*
* - If per-app proxy is not enabled, disallow the VPN service's own package.
* - If no apps are selected, disallow the VPN service's own package.
* - If bypass mode is enabled, disallow all selected apps (including self).
* - If proxy mode is enabled, only allow the selected apps (excluding self).
*
* @param builder The VPN Builder to configure.
*/
private fun configurePerAppProxy(builder: Builder) {
val selfPackageName = BuildConfig.APPLICATION_ID
// If per-app proxy is not enabled, disallow the VPN service's own package and return
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_PER_APP_PROXY) == false) {
builder.addDisallowedApplication(selfPackageName)
return
}
// If no apps are selected, disallow the VPN service's own package and return
val apps = MmkvManager.decodeSettingsStringSet(AppConfig.PREF_PER_APP_PROXY_SET)
if (apps.isNullOrEmpty()) {
builder.addDisallowedApplication(selfPackageName)
return
}
val bypassApps = MmkvManager.decodeSettingsBool(AppConfig.PREF_BYPASS_APPS)
// Handle the VPN service's own package according to the mode
if (bypassApps) apps.add(selfPackageName) else apps.remove(selfPackageName)
apps.forEach {
try {
if (bypassApps) {
// In bypass mode, disallow the selected apps
builder.addDisallowedApplication(it)
} else {
// In proxy mode, only allow the selected apps
builder.addAllowedApplication(it)
}
} catch (e: PackageManager.NameNotFoundException) {
Log.e(AppConfig.TAG, "Failed to configure app in VPN: ${e.localizedMessage}", e)
}
}
}
/**
* Runs the tun2socks process.
* Starts the tun2socks process with the appropriate parameters.

View File

@@ -385,6 +385,11 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
true
}
R.id.intelligent_selection_all -> {
mainViewModel.createIntelligentSelectionAll()
true
}
R.id.service_restart -> {
restartV2Ray()
true

View File

@@ -258,6 +258,7 @@ 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
).forEach { key ->
if (MmkvManager.decodeSettingsString(key) != null) {

View File

@@ -45,6 +45,7 @@ class SubEditActivity : BaseActivity() {
binding.etRemarks.text = Utils.getEditable(subItem.remarks)
binding.etUrl.text = Utils.getEditable(subItem.url)
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
@@ -60,6 +61,7 @@ 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
@@ -75,6 +77,7 @@ class SubEditActivity : BaseActivity() {
subItem.remarks = binding.etRemarks.text.toString()
subItem.url = binding.etUrl.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()

View File

@@ -384,6 +384,29 @@ 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<AngApplication>(),
serversCache.map { it.guid }.toList(),
subscriptionId
)
launch(Dispatchers.Main) {
if (key.isNullOrEmpty()) {
getApplication<AngApplication>().toastError(R.string.toast_failure)
} else {
getApplication<AngApplication>().toastSuccess(R.string.toast_success)
MmkvManager.setSelectServer(key)
reloadServerList()
}
}
}
}
/**
* Initializes assets.
* @param assets The asset manager.

View File

@@ -50,6 +50,7 @@ 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,

View File

@@ -93,6 +93,25 @@
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/padding_spacing_dp16"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/sub_setting_intelligent_selection_filter" />
<EditText
android:id="@+id/et_intelligent_selection_filter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@@ -82,6 +82,10 @@
android:icon="@drawable/ic_share_24dp"
android:title="@string/title_export_all"
app:showAsAction="never" />
<item
android:id="@+id/intelligent_selection_all"
android:title="@string/title_create_intelligent_selection_all_server"
app:showAsAction="never" />
<item
android:id="@+id/ping_all"
android:title="@string/title_ping_all_server"

View File

@@ -269,6 +269,7 @@
<string name="title_sub_update">تحديث الاشتراك (أول خطوة)</string>
<string name="title_ping_all_server">Tcping لجميع الإعدادات</string>
<string name="title_real_ping_all_server"> اختبر جميع الإعدادات (3)</string>
<string name="title_create_intelligent_selection_all_server">Creating Intelligent Selection Current Group Configuration</string>
<string name="title_user_asset_setting">Asset files</string>
<string name="title_sort_by_test_results">الفرز حسب نتائج الاختبار (5)</string>
<string name="title_filter_config">تصفية ملف التكوين</string>
@@ -363,5 +364,12 @@
<item>Resolve and add to DNS Hosts</item>
<item>Resolve and replace domain</item>
</string-array>
<string name="intelligent_selection">Intelligent Selection</string>
<string name="sub_setting_intelligent_selection_filter">Remarks Intelligent Selection regular filter</string>
<string name="title_intelligent_selection_method">Intelligent Selection Method</string>
<string-array name="intelligent_selection_method">
<item>Least Ping</item>
<item>Least Load</item>
</string-array>
</resources>

View File

@@ -269,6 +269,7 @@
<string name="title_sub_update">সাবস্ক্রিপশন আপডেট</string>
<string name="title_ping_all_server">সব কনফিগারেশন TCPing</string>
<string name="title_real_ping_all_server">সব কনফিগারেশন প্রকৃত বিলম্ব</string>
<string name="title_create_intelligent_selection_all_server">Creating Intelligent Selection Current Group Configuration</string>
<string name="title_user_asset_setting">Asset files</string>
<string name="title_sort_by_test_results">টেস্ট ফলাফল দ্বারা সাজানো</string>
<string name="title_filter_config">কনফিগারেশন ফাইল ফিল্টার করুন</string>
@@ -368,5 +369,12 @@
<item>Resolve and add to DNS Hosts</item>
<item>Resolve and replace domain</item>
</string-array>
<string name="intelligent_selection">Intelligent Selection</string>
<string name="sub_setting_intelligent_selection_filter">Remarks Intelligent Selection regular filter</string>
<string name="title_intelligent_selection_method">Intelligent Selection Method</string>
<string-array name="intelligent_selection_method">
<item>Least Ping</item>
<item>Least Load</item>
</string-array>
</resources>

View File

@@ -159,13 +159,13 @@
<item>گوم زیڌن</item>
</string-array>
<string name="title_pref_speed_enabled">ر وندن نشۉݩ داڌن سورعت</string>
<string name="summary_pref_speed_enabled">نشۉݩ داڌن سورعت هیم سکویی من وارسۊویا. نماڌ وارسۊوی و ری و کار گرؽڌن آلشت ابۊ.</string>
<string name="title_pref_speed_enabled">ر وندن نشووݩ داڌن سورعت</string>
<string name="summary_pref_speed_enabled">نشووݩ داڌن سورعت هیم سکویی من وارسۊویا. نماڌ وارسۊوی و ری و کار گرؽڌن آلشت ابۊ.</string>
<string name="title_pref_sniffing_enabled">ر وندن Sniffing</string>
<string name="summary_pref_sniffing_enabled">دامنه sniff ن ز کتن امتهۉݩ کۊنین (پؽش فرز رۊشن)</string>
<string name="summary_pref_sniffing_enabled">دامنه sniff ن ز کتن امتهووݩ کۊنین (پؽش فرز رۊشن)</string>
<string name="title_pref_route_only_enabled">ر وندن routeOnly</string>
<string name="summary_pref_route_only_enabled">ز نوم دامنه sniffed تینا سی تور جوستن استفاڌه کۊنین وو نشۊوی مۉرد نزرن و عونوان نشۊوی IP ووردارین.</string>
<string name="summary_pref_route_only_enabled">ز نوم دامنه sniffed تینا سی تور جوستن استفاڌه کۊنین وو نشۊوی موورد نزرن و عونوان نشۊوی IP ووردارین.</string>
<string name="title_pref_local_dns_enabled">ر وندن DNS مهلی</string>
<string name="summary_pref_local_dns_enabled">درخاستا DNS و هسته و من ایان وو و دست ماژول DNS پردازشت ابۊن (پؽشنهاڌ ابۊ ٱر لنگ تور جوستن سی دور زیڌن نشۊویا LAN وو وولات ٱسلی هڌین فعال بۊ)</string>
@@ -176,18 +176,18 @@
<string name="title_pref_prefer_ipv6">ترجی IPv6</string>
<string name="summary_pref_prefer_ipv6">تورا IPv6 ن فعال کۊنین وو نشۊویا IPv6 ن ترجی بڌین</string>
<string name="title_pref_remote_dns">ز ر دیر (اختیاری) DNS (udp/tcp/https/quic) (اختیاری)</string>
<string name="title_pref_remote_dns">DNS ز ر دیر (اختیاری) (udp/tcp/https/quic) (اختیاری)</string>
<string name="summary_pref_remote_dns">DNS</string>
<string name="title_pref_vpn_dns">VPN DNS (تینا IPv4/v6)</string>
<string name="title_pref_vpn_bypass_lan">ز شبکه مهلی اگوڌرته؟ VPN</string>
<string name="title_pref_vpn_bypass_lan">VPN ز شبکه مهلی اگوڌرته؟</string>
<string name="title_pref_vpn_interface_address">نشۊوی رابت VPN</string>
<string name="title_pref_domestic_dns">منی (اختیاری) DNS</string>
<string name="title_pref_domestic_dns">DNS منی (اختیاری)</string>
<string name="summary_pref_domestic_dns">DNS</string>
<string name="title_pref_dns_hosts">هاست موستقیم (قالوو: دامنه: نشۊوی،...) DNS</string>
<string name="title_pref_dns_hosts">DNS هاست موستقیم (قالوو: دامنه: نشۊوی،...)</string>
<string name="summary_pref_dns_hosts">دامنه:نشۊوی،...</string>
<string name="title_pref_delay_test_url">نشۊوی اینترنتی آزمایش تئخیر واقعی (http/https)</string>
@@ -215,8 +215,8 @@
<string name="title_pref_append_http_proxy">پروکسی HTTP ن و VPN ازاف کۊنین</string>
<string name="summary_pref_append_http_proxy">پروکسی HTTP ن موسقیمن ز (مۊرۊرگر/ی قرد ز برنومه یل لادراری بیڌه)، بؽ استفاڌه ز دسگا NIC مجازی (Android 10+) استفاڌه ابۊ.</string>
<string name="title_pref_double_column_display">ر وندن نشۉݩ داڌن دو سۊتۊنی</string>
<string name="summary_pref_double_column_display">نومگه نمایه یل من دو سۊتۊن نشۉݩ داڌه ابۊن وو چینۉ ترین موئتوا بیشتری ن سیل کۊنین. سی ر وستن، وا برنومه ن ز نۊ ر ونین.</string>
<string name="title_pref_double_column_display">ر وندن نشووݩ داڌن دو سۊتۊنی</string>
<string name="summary_pref_double_column_display">نومگه نمایه یل من دو سۊتۊن نشووݩ داڌه ابۊن وو چینۉ ترین موئتوا بیشتری ن سیل کۊنین. سی ر وستن، وا برنومه ن ز نۊ ر ونین.</string>
<!-- AboutActivity -->
<string name="title_pref_feedback">فشناڌن منشڌ</string>
@@ -244,7 +244,7 @@
<string name="title_outbound_domain_resolve_method">بارت پؽش هل دامنه دری</string>
<string name="title_mode">هالت</string>
<string name="title_mode_help">سی دووسمندیا وو هیاری بیشتر، ری ای هؽل بزݩ</string>
<string name="title_language">زۉݩ</string>
<string name="title_language">زووݩ</string>
<string name="title_ui_settings">سامووا رابت منتوری</string>
<string name="title_pref_ui_mode_night">سامووا هالت رابت منتوری</string>
@@ -269,6 +269,7 @@
<string name="title_sub_update">ورۊ کردن اشتراک جرگه سکویی</string>
<string name="title_ping_all_server">Tcping کانفیگا جرگه سکویی</string>
<string name="title_real_ping_all_server">تئخیر واقعی کانفیگا جرگه سکویی</string>
<string name="title_create_intelligent_selection_all_server">وورکل پسند هۊشمند کانفیگ جرگه سکویی</string>
<string name="title_user_asset_setting">فایلا بونچک جوقرافیایی</string>
<string name="title_sort_by_test_results">ترتیب و ری نتیجه یل آزمایش</string>
<string name="title_filter_config">فیلتر کردن کانفیگا</string>
@@ -378,5 +379,12 @@
<item>هل وو ٱووردن و میزبووݩ یل دامنه DNS</item>
<item>هل وو جایونی دامنه</item>
</string-array>
<string name="intelligent_selection">پسند هۊشمند</string>
<string name="sub_setting_intelligent_selection_filter">گوڌنا دیاری پسند هۊشمند فیلتر مئمۊلی</string>
<string name="title_intelligent_selection_method">بارت پسند هۊشمند</string>
<string-array name="intelligent_selection_method">
<item>بلم ترین پینگ</item>
<item>هدقل بار</item>
</string-array>
</resources>

View File

@@ -266,6 +266,7 @@
<string name="title_sub_update">به‌روزرسانی گروه فعلی اشتراک</string>
<string name="title_ping_all_server">TCPING کانفیگ های گروه فعلی</string>
<string name="title_real_ping_all_server">تاخیر واقعی کانفیگ های گروه فعلی</string>
<string name="title_create_intelligent_selection_all_server">Creating Intelligent Selection Current Group Configuration</string>
<string name="title_user_asset_setting">فایل های منبع جغرافیایی</string>
<string name="title_sort_by_test_results">مرتب‌ سازی بر اساس نتایج آزمایش</string>
<string name="title_filter_config">فیلتر کردن کانفیگ‌ها</string>
@@ -377,5 +378,12 @@
<item>Resolve and add to DNS Hosts</item>
<item>Resolve and replace domain</item>
</string-array>
<string name="intelligent_selection">Intelligent Selection</string>
<string name="sub_setting_intelligent_selection_filter">Remarks Intelligent Selection regular filter</string>
<string name="title_intelligent_selection_method">Intelligent Selection Method</string>
<string-array name="intelligent_selection_method">
<item>Least Ping</item>
<item>Least Load</item>
</string-array>
</resources>

View File

@@ -153,9 +153,9 @@
<string name="title_pref_mux_xudp_concurency">XUDP-соединения (диапазон от 1 до 1024)</string>
<string name="title_pref_mux_xudp_quic">Обработка QUIC в мультиплексном туннеле</string>
<string-array name="mux_xudp_quic_entries">
<item>отклонять</item>
<item>разрешать</item>
<item>пропускать</item>
<item>Отклонять</item>
<item>Разрешать</item>
<item>Пропускать</item>
</string-array>
<string name="title_pref_speed_enabled">Отображение скорости</string>
@@ -179,7 +179,7 @@
<string name="summary_pref_remote_dns">DNS</string>
<string name="title_pref_vpn_dns">VPN DNS (только IPv4/v6)</string>
<string name="title_pref_vpn_bypass_lan">VPN пропускает LAN</string>
<string name="title_pref_vpn_bypass_lan">VPN обходит LAN</string>
<string name="title_pref_vpn_interface_address">VPN частный IP</string>
@@ -240,7 +240,7 @@
<string name="title_pref_auto_update_interval">Интервал автообновления (минут, не менее 15)</string>
<string name="title_core_loglevel">Подробность ведения журнала</string>
<string name="title_outbound_domain_resolve_method">Outbound domain pre-resolve method</string>
<string name="title_outbound_domain_resolve_method">Предварительное определение исходящего домена</string>
<string name="title_mode">Режим</string>
<string name="title_mode_help">Нажмите для получения дополнительной информации</string>
<string name="title_language">Язык</string>
@@ -266,10 +266,11 @@
<string name="sub_setting_next_profile">Следующая конфигурация прокси</string>
<string name="sub_setting_pre_profile_tip">Конфигурация должна быть уникальной</string>
<string name="title_sub_update">Обновить подписку группы</string>
<string name="title_ping_all_server">Проверка профилей группы</string>
<string name="title_ping_all_server">Проверить профили группы</string>
<string name="title_real_ping_all_server">Время отклика профилей группы</string>
<string name="title_create_intelligent_selection_all_server">Создать конфигурацию умного выбора</string>
<string name="title_user_asset_setting">Файлы ресурсов</string>
<string name="title_sort_by_test_results">Сортировка по результатам теста</string>
<string name="title_sort_by_test_results">Сортировать по результатам теста</string>
<string name="title_filter_config">Фильтр групп</string>
<string name="filter_config_all">Все группы</string>
<string name="title_del_duplicate_config_count">Удалено дубликатов профилей: %d</string>
@@ -368,14 +369,21 @@
<string-array name="vpn_bypass_lan">
<item>Как в профиле</item>
<item>Пропускает</item>
<item>Не пропускает</item>
<item>Обходит</item>
<item>Не обходит</item>
</string-array>
<string-array name="outbound_domain_resolve_method">
<item>Do not resolve</item>
<item>Resolve and add to DNS Hosts</item>
<item>Resolve and replace domain</item>
<item>Не определять</item>
<item>Определять и добавлять к узлам DNS</item>
<item>Определять и заменять домен</item>
</string-array>
<string name="intelligent_selection">Умный выбор</string>
<string name="sub_setting_intelligent_selection_filter">Название фильтра умного выбора</string>
<string name="title_intelligent_selection_method">Принцип умного выбора</string>
<string-array name="intelligent_selection_method">
<item>Наименьшая задержка</item>
<item>Наименьшая нагрузка</item>
</string-array>
</resources>

View File

@@ -269,6 +269,7 @@
<string name="title_sub_update">Cập nhật các gói đăng ký</string>
<string name="title_ping_all_server">Ping tất cả máy chủ</string>
<string name="title_real_ping_all_server">Kiểm tra HTTP tất cả máy chủ</string>
<string name="title_create_intelligent_selection_all_server">Creating Intelligent Selection Current Group Configuration</string>
<string name="title_user_asset_setting">Asset files</string>
<string name="title_sort_by_test_results">Sắp xếp lại theo lần kiểm tra cuối cùng</string>
<string name="title_filter_config">Lọc cấu hình theo các gói đăng ký</string>
@@ -365,5 +366,12 @@
<item>Resolve and add to DNS Hosts</item>
<item>Resolve and replace domain</item>
</string-array>
<string name="intelligent_selection">Intelligent Selection</string>
<string name="sub_setting_intelligent_selection_filter">Remarks Intelligent Selection regular filter</string>
<string name="title_intelligent_selection_method">Intelligent Selection Method</string>
<string-array name="intelligent_selection_method">
<item>Least Ping</item>
<item>Least Load</item>
</string-array>
</resources>

View File

@@ -266,6 +266,7 @@
<string name="title_sub_update">更新当前组订阅</string>
<string name="title_ping_all_server">测试当前组配置 Tcping</string>
<string name="title_real_ping_all_server">测试当前组配置真连接</string>
<string name="title_create_intelligent_selection_all_server">生成当前组智能选择配置</string>
<string name="title_user_asset_setting">资源文件</string>
<string name="title_sort_by_test_results">按测试结果排序</string>
<string name="title_filter_config">过滤配置文件</string>
@@ -369,5 +370,12 @@
<item>解析后添加至 DNS Hosts</item>
<item>解析后替换原域名</item>
</string-array>
<string name="intelligent_selection">智能选择</string>
<string name="sub_setting_intelligent_selection_filter">别名智能选择正则过滤</string>
<string name="title_intelligent_selection_method">智能选择方式</string>
<string-array name="intelligent_selection_method">
<item>最低延迟</item>
<item>最稳定</item>
</string-array>
</resources>

View File

@@ -267,6 +267,7 @@
<string name="title_sub_update">更新目前群組訂閱</string>
<string name="title_ping_all_server">偵測目前群組設定 Tcping</string>
<string name="title_real_ping_all_server">偵測目前群組設定真延遲</string>
<string name="title_create_intelligent_selection_all_server">Creating Intelligent Selection Current Group Configuration</string>
<string name="title_user_asset_setting">資源檔案</string>
<string name="title_sort_by_test_results">依偵測結果排序</string>
<string name="title_filter_config">過濾設定</string>
@@ -369,5 +370,12 @@
<item>解析後加入 DNS Hosts</item>
<item>解析後替換原網域名稱</item>
</string-array>
<string name="intelligent_selection">Intelligent Selection</string>
<string name="sub_setting_intelligent_selection_filter">Remarks Intelligent Selection regular filter</string>
<string name="title_intelligent_selection_method">Intelligent Selection Method</string>
<string-array name="intelligent_selection_method">
<item>Least Ping</item>
<item>Least Load</item>
</string-array>
</resources>

View File

@@ -208,4 +208,9 @@
<item>2</item>
</string-array>
<string-array name="intelligent_selection_method_value" translatable="false">
<item>0</item>
<item>1</item>
</string-array>
</resources>

View File

@@ -270,6 +270,7 @@
<string name="title_sub_update">Update current group subscription</string>
<string name="title_ping_all_server">Tcping current group configuration</string>
<string name="title_real_ping_all_server">Real delay current group configuration</string>
<string name="title_create_intelligent_selection_all_server">Creating Intelligent Selection Current Group Configuration</string>
<string name="title_user_asset_setting">Asset files</string>
<string name="title_sort_by_test_results">Sorting by test results</string>
<string name="title_filter_config">Filter configuration file</string>
@@ -379,5 +380,12 @@
<item>Resolve and add to DNS Hosts</item>
<item>Resolve and replace domain</item>
</string-array>
<string name="intelligent_selection">Intelligent Selection</string>
<string name="sub_setting_intelligent_selection_filter">Remarks Intelligent Selection regular filter</string>
<string name="title_intelligent_selection_method">Intelligent Selection Method</string>
<string-array name="intelligent_selection_method">
<item>Least Ping</item>
<item>Least Load</item>
</string-array>
</resources>

View File

@@ -230,6 +230,14 @@
android:summary="%s"
android:title="@string/title_outbound_domain_resolve_method" />
<ListPreference
android:defaultValue="0"
android:entries="@array/intelligent_selection_method"
android:entryValues="@array/intelligent_selection_method_value"
android:key="pref_intelligent_selection_method"
android:summary="%s"
android:title="@string/title_intelligent_selection_method" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/title_advanced">

View File

@@ -21,7 +21,7 @@ toasty = "1.5.2"
editorkit = "2.9.0"
core = "3.5.3"
workRuntimeKtx = "2.10.2"
lifecycleViewmodelKtx = "2.9.1"
lifecycleViewmodelKtx = "2.9.2"
multidex = "2.0.1"
mockitoMockitoInline = "5.2.0"
flexbox = "3.0.0"