From 358713a2a3cf8d10454f814da0216afc109dbead Mon Sep 17 00:00:00 2001 From: solokot Date: Fri, 11 Apr 2025 04:19:02 +0300 Subject: [PATCH 01/81] Update Russian translation (#4484) --- V2rayNG/app/src/main/res/values-ru/strings.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/V2rayNG/app/src/main/res/values-ru/strings.xml b/V2rayNG/app/src/main/res/values-ru/strings.xml index 9cf4fd3e..168d29da 100644 --- a/V2rayNG/app/src/main/res/values-ru/strings.xml +++ b/V2rayNG/app/src/main/res/values-ru/strings.xml @@ -257,9 +257,9 @@ Название фильтра Использовать обновление Использовать автообновление - Название предыдущего прокси - Название следующего прокси - Название должно существовать и быть уникальным + Предыдущая конфигурация прокси + Следующая конфигурация прокси + Конфигурация должна быть уникальной Обновить подписку группы Проверка профилей группы Время отклика профилей группы @@ -321,7 +321,7 @@ Установлена последняя версия Найдена новая версия: %s Обновить - Проверить предварительный выпуск + Искать предварительный выпуск QR-код From 38193b56216f332eb20b84a8e1395c9b4a1d7f42 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Mon, 14 Apr 2025 14:15:25 +0800 Subject: [PATCH 02/81] Added IP display in connection test https://github.com/2dust/v2rayNG/issues/4489 --- V2rayNG/app/src/main/java/com/v2ray/ang/AppConfig.kt | 1 + .../app/src/main/java/com/v2ray/ang/dto/IPAPIInfo.kt | 11 +++++++++++ .../java/com/v2ray/ang/handler/SpeedtestManager.kt | 10 ++++++++++ .../java/com/v2ray/ang/service/V2RayServiceManager.kt | 8 +++++++- V2rayNG/app/src/main/res/values/strings.xml | 2 +- 5 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 V2rayNG/app/src/main/java/com/v2ray/ang/dto/IPAPIInfo.kt 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 0a2a2946..6a6195d5 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/AppConfig.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/AppConfig.kt @@ -103,6 +103,7 @@ object AppConfig { const val TG_CHANNEL_URL = "https://t.me/github_2dust" const val DELAY_TEST_URL = "https://www.gstatic.com/generate_204" const val DELAY_TEST_URL2 = "https://www.google.com/generate_204" + const val IP_API_Url = "https://api.ip.sb/geoip" /** DNS server addresses. */ const val DNS_PROXY = "1.1.1.1" diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/IPAPIInfo.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/IPAPIInfo.kt new file mode 100644 index 00000000..4d29ffc7 --- /dev/null +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/IPAPIInfo.kt @@ -0,0 +1,11 @@ +package com.v2ray.ang.dto + +data class IPAPIInfo( + var ip: String? = null, + var city: String? = null, + var region: String? = null, + var region_code: String? = null, + var country: String? = null, + var country_name: String? = null, + var country_code: String? = null +) \ No newline at end of file diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SpeedtestManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SpeedtestManager.kt index bab1f1d4..99898a3a 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SpeedtestManager.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SpeedtestManager.kt @@ -6,8 +6,10 @@ import android.text.TextUtils import android.util.Log import com.v2ray.ang.AppConfig import com.v2ray.ang.R +import com.v2ray.ang.dto.IPAPIInfo import com.v2ray.ang.extension.responseLength import com.v2ray.ang.util.HttpUtil +import com.v2ray.ang.util.JsonUtil import kotlinx.coroutines.isActive import libv2ray.Libv2ray import java.io.IOException @@ -164,6 +166,14 @@ object SpeedtestManager { return Pair(elapsed, result) } + fun getRemoteIPInfo(): String? { + val httpPort = SettingsManager.getHttpPort() + var content = HttpUtil.getUrlContent(AppConfig.IP_API_Url, 5000, httpPort) ?: return null + + var ipInfo = JsonUtil.fromJson(content, IPAPIInfo::class.java) ?: return null + return "(${ipInfo.country_code}) ${ipInfo.ip}" + } + /** * Gets the version of the V2Ray library. * diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt index d1c17cf1..44cd257a 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt @@ -15,6 +15,7 @@ import com.v2ray.ang.dto.ProfileItem import com.v2ray.ang.extension.toast import com.v2ray.ang.handler.MmkvManager import com.v2ray.ang.handler.SettingsManager +import com.v2ray.ang.handler.SpeedtestManager import com.v2ray.ang.handler.V2rayConfigManager import com.v2ray.ang.util.MessageUtil import com.v2ray.ang.util.PluginUtil @@ -231,7 +232,12 @@ object V2RayServiceManager { val result = if (time == -1L) { service.getString(R.string.connection_test_error, errstr) } else { - service.getString(R.string.connection_test_available, time) + buildString { + append(service.getString(R.string.connection_test_available, time)) + SpeedtestManager.getRemoteIPInfo()?.let { ip -> + append("\n$ip") + } + } } MessageUtil.sendMsg2UI(service, AppConfig.MSG_MEASURE_DELAY_SUCCESS, result) diff --git a/V2rayNG/app/src/main/res/values/strings.xml b/V2rayNG/app/src/main/res/values/strings.xml index ced93fa0..c1b31042 100644 --- a/V2rayNG/app/src/main/res/values/strings.xml +++ b/V2rayNG/app/src/main/res/values/strings.xml @@ -304,7 +304,7 @@ Check Connectivity Testing… Testing %d configurations… - Success: HTTP connection took %dms + Success: Connection took %dms Fail to detect internet connection: %s Internet Unavailable Error code: #%d From 773ddc5373a93ac99ee14d88ce7dd5bf34637f0b Mon Sep 17 00:00:00 2001 From: DHR60 Date: Mon, 14 Apr 2025 20:42:02 +0800 Subject: [PATCH 03/81] Fix wg chain proxy (#4496) * Fix handle null streamSettings in WireGuard chained proxy * Allows null preshared key for WireGuard * remove WIREGUARD_LOCAL_ADDRESS_V6 * Considers WireGuard outbound for domain port --- .../src/main/java/com/v2ray/ang/dto/V2rayConfig.kt | 2 +- .../src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt | 8 ++++---- .../com/v2ray/ang/handler/V2rayConfigManager.kt | 13 +++++++++++-- .../main/java/com/v2ray/ang/ui/ServerActivity.kt | 5 ++--- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt index 692caf69..a3323d87 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt @@ -197,7 +197,7 @@ data class V2rayConfig( data class WireGuardBean( var publicKey: String = "", - var preSharedKey: String = "", + var preSharedKey: String? = null, var endpoint: String = "" ) } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt index 3d58ad40..e2bc5f90 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt @@ -31,7 +31,7 @@ object WireguardFmt : FmtBase() { config.secretKey = uri.userInfo.orEmpty() config.localAddress = queryParam["address"] ?: WIREGUARD_LOCAL_ADDRESS_V4 config.publicKey = queryParam["publickey"].orEmpty() - config.preSharedKey = queryParam["presharedkey"].orEmpty() + config.preSharedKey = queryParam["presharedkey"]?.takeIf { it.isNotEmpty() } config.mtu = Utils.parseInt(queryParam["mtu"] ?: AppConfig.WIREGUARD_LOCAL_MTU) config.reserved = queryParam["reserved"] ?: "0,0,0" @@ -83,7 +83,7 @@ object WireguardFmt : FmtBase() { config.localAddress = interfaceParams["address"] ?: WIREGUARD_LOCAL_ADDRESS_V4 config.mtu = Utils.parseInt(interfaceParams["mtu"] ?: AppConfig.WIREGUARD_LOCAL_MTU) config.publicKey = peerParams["publickey"].orEmpty() - config.preSharedKey = peerParams["presharedkey"].orEmpty() + config.preSharedKey = peerParams["presharedkey"]?.takeIf { it.isNotEmpty() } val endpoint = peerParams["endpoint"].orEmpty() val endpointParts = endpoint.split(":", limit = 2) if (endpointParts.size == 2) { @@ -112,11 +112,11 @@ object WireguardFmt : FmtBase() { wireguard.address = (profileItem.localAddress ?: WIREGUARD_LOCAL_ADDRESS_V4).split(",") wireguard.peers?.firstOrNull()?.let { peer -> peer.publicKey = profileItem.publicKey.orEmpty() - peer.preSharedKey = profileItem.preSharedKey.orEmpty() + peer.preSharedKey = profileItem.preSharedKey?.takeIf { it.isNotEmpty() } peer.endpoint = Utils.getIpv6Address(profileItem.server) + ":${profileItem.serverPort}" } wireguard.mtu = profileItem.mtu - wireguard.reserved = profileItem.reserved?.split(",")?.map { it.toInt() } + wireguard.reserved = profileItem.reserved?.takeIf { it.isNotBlank() }?.split(",")?.filter { it.isNotBlank() }?.map { it.trim().toInt() } } return outboundBean 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 0a08b1ec..ffd40eda 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 @@ -30,7 +30,6 @@ import com.v2ray.ang.AppConfig.TAG_DIRECT import com.v2ray.ang.AppConfig.TAG_FRAGMENT import com.v2ray.ang.AppConfig.TAG_PROXY import com.v2ray.ang.AppConfig.WIREGUARD_LOCAL_ADDRESS_V4 -import com.v2ray.ang.AppConfig.WIREGUARD_LOCAL_ADDRESS_V6 import com.v2ray.ang.dto.ConfigResult import com.v2ray.ang.dto.EConfigType import com.v2ray.ang.dto.NetworkType @@ -561,7 +560,7 @@ object V2rayConfigManager { if (protocol.equals(EConfigType.WIREGUARD.name, true)) { var localTunAddr = if (outbound.settings?.address == null) { - listOf(WIREGUARD_LOCAL_ADDRESS_V4, WIREGUARD_LOCAL_ADDRESS_V6) + listOf(WIREGUARD_LOCAL_ADDRESS_V4) } else { outbound.settings?.address as List<*> } @@ -700,6 +699,9 @@ object V2rayConfigManager { updateOutboundWithGlobalSettings(prevOutbound) prevOutbound.tag = TAG_PROXY + "2" v2rayConfig.outbounds.add(prevOutbound) + if (outbound.streamSettings == null) { + outbound.streamSettings = V2rayConfig.OutboundBean.StreamSettingsBean() + } outbound.streamSettings?.sockopt = V2rayConfig.OutboundBean.StreamSettingsBean.SockoptBean( dialerProxy = prevOutbound.tag @@ -717,10 +719,17 @@ object V2rayConfigManager { nextOutbound.tag = TAG_PROXY v2rayConfig.outbounds.add(0, nextOutbound) outbound.tag = TAG_PROXY + "1" + if (nextOutbound.streamSettings == null) { + nextOutbound.streamSettings = V2rayConfig.OutboundBean.StreamSettingsBean() + } nextOutbound.streamSettings?.sockopt = V2rayConfig.OutboundBean.StreamSettingsBean.SockoptBean( dialerProxy = outbound.tag ) + if (nextNode.configType == EConfigType.WIREGUARD) + { + domainPort = nextNode.getServerAddressAndPort() + } } } } catch (e: Exception) { diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ServerActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ServerActivity.kt index f381e570..e9bdad66 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ServerActivity.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ServerActivity.kt @@ -18,7 +18,6 @@ import com.v2ray.ang.AppConfig.PREF_ALLOW_INSECURE import com.v2ray.ang.AppConfig.REALITY import com.v2ray.ang.AppConfig.TLS import com.v2ray.ang.AppConfig.WIREGUARD_LOCAL_ADDRESS_V4 -import com.v2ray.ang.AppConfig.WIREGUARD_LOCAL_ADDRESS_V6 import com.v2ray.ang.AppConfig.WIREGUARD_LOCAL_MTU import com.v2ray.ang.R import com.v2ray.ang.dto.EConfigType @@ -327,7 +326,7 @@ class ServerActivity : BaseActivity() { et_preshared_key?.text = Utils.getEditable(config.preSharedKey.orEmpty()) et_reserved1?.text = Utils.getEditable(config.reserved ?: "0,0,0") et_local_address?.text = Utils.getEditable( - config.localAddress ?: "$WIREGUARD_LOCAL_ADDRESS_V4,$WIREGUARD_LOCAL_ADDRESS_V6" + config.localAddress ?: WIREGUARD_LOCAL_ADDRESS_V4 ) et_local_mtu?.text = Utils.getEditable(config.mtu?.toString() ?: WIREGUARD_LOCAL_MTU) } else if (config.configType == EConfigType.HYSTERIA2) { @@ -420,7 +419,7 @@ class ServerActivity : BaseActivity() { et_public_key?.text = null et_reserved1?.text = Utils.getEditable("0,0,0") et_local_address?.text = - Utils.getEditable("${WIREGUARD_LOCAL_ADDRESS_V4},${WIREGUARD_LOCAL_ADDRESS_V6}") + Utils.getEditable(WIREGUARD_LOCAL_ADDRESS_V4) et_local_mtu?.text = Utils.getEditable(WIREGUARD_LOCAL_MTU) return true } From 9cb28ed969361cf074c76db84e2af265f3983e59 Mon Sep 17 00:00:00 2001 From: Pk-web6936 <202365630+Pk-web6936@users.noreply.github.com> Date: Tue, 15 Apr 2025 09:44:13 +0330 Subject: [PATCH 04/81] Update Persian translate (#4495) https://github.com/2dust/v2rayNG/commit/38193b56216f332eb20b84a8e1395c9b4a1d7f42 --- V2rayNG/app/src/main/res/values-fa/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/V2rayNG/app/src/main/res/values-fa/strings.xml b/V2rayNG/app/src/main/res/values-fa/strings.xml index c739fd86..7b81716c 100644 --- a/V2rayNG/app/src/main/res/values-fa/strings.xml +++ b/V2rayNG/app/src/main/res/values-fa/strings.xml @@ -300,7 +300,7 @@ اتصال را بررسی کنید در حال آزمایش... تست کردن %d کانفیگ… - موفقیت: اتصال HTTP %dms طول کشید + موفقیت: اتصال %dms طول کشید اتصال به اینترنت شناسایی نشد: %s اینترنت در دسترس نیست کد خطا: #%d From 3f9bc098ece64798aa59270ddf763d984dafebba Mon Sep 17 00:00:00 2001 From: kore kas nadar <63148255+korekasnadar@users.noreply.github.com> Date: Tue, 15 Apr 2025 14:51:17 +0330 Subject: [PATCH 05/81] Update Luri Bakhtiari translation (#4497) --- V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 0275de46..1805f237 100644 --- a/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml +++ b/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml @@ -303,7 +303,7 @@ منپیزن واجۊری کوݩ هونی آزمایش ابۊ… %d کانفیگ هونی آزمایش ابۊ... - مووفق بی: منپیز HTTP %dms تۊل کشی + مووفق بی: منپیز %dms تۊل کشی منپیز و اینترنتن نجوست: %s اینترنت من دسرس نؽ کود ختا: #%d @@ -322,7 +322,7 @@ سکو نوسخه دیندایی پۊرنیڌه هڌ نوسخه نۊ ن جوست: %s سکو ورۊ رسۊوی کۊنین - نوسخیل پؽش ز تیجنیڌنن واجۊری کۊنین + واجۊری نوسخه یل پؽش ز تیجنیڌن QRcode From 4084ae29389d1f316d5579e6e7cd8b5e0b3334a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=A1=A0=E1=A0=B5=E1=A1=A0=E1=A1=B3=20=E1=A1=A0=E1=A0=B5?= =?UTF-8?q?=E1=A1=A0=20=E1=A0=AE=E1=A0=A0=E1=A0=A8=E1=A1=A9=E1=A0=8B?= =?UTF-8?q?=E1=A0=A0=E1=A0=A8?= <125150101+UjuiUjuMandan@users.noreply.github.com> Date: Tue, 15 Apr 2025 11:21:25 +0000 Subject: [PATCH 06/81] Updating PRIVATE_IP_LIST (#4498) --- V2rayNG/app/src/main/java/com/v2ray/ang/AppConfig.kt | 2 ++ 1 file changed, 2 insertions(+) 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 6a6195d5..649b71c1 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/AppConfig.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/AppConfig.kt @@ -224,7 +224,9 @@ object AppConfig { ) val PRIVATE_IP_LIST = arrayListOf( + "0.0.0.0/8", "10.0.0.0/8", + "127.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "169.254.0.0/16", From cfd81441fa56705da6d49a6a172791fd166f02a2 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Tue, 15 Apr 2025 20:44:03 +0800 Subject: [PATCH 07/81] Update libs.versions.toml --- V2rayNG/gradle/libs.versions.toml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/V2rayNG/gradle/libs.versions.toml b/V2rayNG/gradle/libs.versions.toml index fb625399..7a3145ef 100644 --- a/V2rayNG/gradle/libs.versions.toml +++ b/V2rayNG/gradle/libs.versions.toml @@ -1,9 +1,9 @@ [versions] agp = "8.9.1" -desugar_jdk_libs = "2.1.5" +desugarJdkLibs = "2.1.5" gradleLicensePlugin = "0.9.8" kotlin = "2.1.20" -coreKtx = "1.15.0" +coreKtx = "1.16.0" junit = "4.13.2" junitVersion = "1.2.1" espressoCore = "3.6.1" @@ -14,8 +14,8 @@ constraintlayout = "2.2.1" mmkvStatic = "1.3.12" gson = "2.12.1" quickieFoss = "1.14.0" -kotlinx-coroutines-android = "1.10.1" -kotlinx-coroutines-core = "1.10.1" +kotlinxCoroutinesAndroid = "1.10.1" +kotlinxCoroutinesCore = "1.10.1" swiperefreshlayout = "1.1.0" toasty = "1.5.2" editorkit = "2.9.0" @@ -30,7 +30,7 @@ recyclerview = "1.4.0" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } androidx-swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version.ref = "swiperefreshlayout" } -desugar_jdk_libs = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desugar_jdk_libs" } +desugar-jdk-libs = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desugarJdkLibs" } gradle-license-plugin = { module = "com.jaredsburrows:gradle-license-plugin", version.ref = "gradleLicensePlugin" } junit = { group = "junit", name = "junit", version.ref = "junit" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } @@ -42,8 +42,8 @@ androidx-constraintlayout = { group = "androidx.constraintlayout", name = "const mmkv-static = { module = "com.tencent:mmkv-static", version.ref = "mmkvStatic" } gson = { module = "com.google.code.gson:gson", version.ref = "gson" } quickie-foss = { module = "com.github.T8RIN.QuickieExtended:quickie-foss", version.ref = "quickieFoss" } -kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version = "kotlinx-coroutines-android" } -kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version = "kotlinx-coroutines-core" } +kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinxCoroutinesAndroid" } +kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "kotlinxCoroutinesCore" } toasty = { module = "com.github.GrenderG:Toasty", version.ref = "toasty" } editorkit = { module = "com.blacksquircle.ui:editorkit", version.ref = "editorkit" } language-base = { module = "com.blacksquircle.ui:language-base", version.ref = "editorkit" } From 963d24ab6642b4dcbf9afb85ee2fb8059b122efa Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Wed, 16 Apr 2025 15:09:49 +0800 Subject: [PATCH 08/81] Optimize and improve https://github.com/2dust/v2rayNG/commit/38193b56216f332eb20b84a8e1395c9b4a1d7f42 --- .../v2ray/ang/service/V2RayServiceManager.kt | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt index 44cd257a..7ef0030b 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt @@ -229,18 +229,19 @@ object V2RayServiceManager { } } } - val result = if (time == -1L) { - service.getString(R.string.connection_test_error, errstr) + val result = if (time >= 0) { + service.getString(R.string.connection_test_available, time) } else { - buildString { - append(service.getString(R.string.connection_test_available, time)) - SpeedtestManager.getRemoteIPInfo()?.let { ip -> - append("\n$ip") - } + service.getString(R.string.connection_test_error, errstr) + } + MessageUtil.sendMsg2UI(service, AppConfig.MSG_MEASURE_DELAY_SUCCESS, result) + + // Only fetch IP info if the delay test was successful + if (time >= 0) { + SpeedtestManager.getRemoteIPInfo()?.let { ip -> + MessageUtil.sendMsg2UI(service, AppConfig.MSG_MEASURE_DELAY_SUCCESS, "$result\n$ip") } } - - MessageUtil.sendMsg2UI(service, AppConfig.MSG_MEASURE_DELAY_SUCCESS, result) } } From 6ca3eb769ef4450663bfabd768158ca7c1d79265 Mon Sep 17 00:00:00 2001 From: Pk-web6936 <202365630+Pk-web6936@users.noreply.github.com> Date: Wed, 16 Apr 2025 15:36:44 +0330 Subject: [PATCH 09/81] Update dependencies (#4485) * Update libs.versions.toml * Update libs.versions.toml * Update libs.versions.toml * Update libs.versions.toml * Update libs.versions.toml * Update libs.versions.toml --- V2rayNG/gradle/libs.versions.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/V2rayNG/gradle/libs.versions.toml b/V2rayNG/gradle/libs.versions.toml index 7a3145ef..76d7c562 100644 --- a/V2rayNG/gradle/libs.versions.toml +++ b/V2rayNG/gradle/libs.versions.toml @@ -12,9 +12,9 @@ material = "1.12.0" activity = "1.10.1" constraintlayout = "2.2.1" mmkvStatic = "1.3.12" -gson = "2.12.1" +gson = "2.13.0" quickieFoss = "1.14.0" -kotlinxCoroutinesAndroid = "1.10.1" +kotlinxCoroutinesAndroid = "1.10.2" kotlinxCoroutinesCore = "1.10.1" swiperefreshlayout = "1.1.0" toasty = "1.5.2" From f0c0e2e83a477963f24f5a9e0f47a90c7352a144 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Thu, 17 Apr 2025 10:41:30 +0800 Subject: [PATCH 10/81] Refactor reference code with libv2ray --- .../ang/service/V2RayProxyOnlyService.kt | 4 +- .../v2ray/ang/service/V2RayServiceManager.kt | 139 ++++++++++-------- .../com/v2ray/ang/service/V2RayTestService.kt | 2 +- .../com/v2ray/ang/service/V2RayVpnService.kt | 19 ++- 4 files changed, 93 insertions(+), 71 deletions(-) diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayProxyOnlyService.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayProxyOnlyService.kt index 66d74e86..25fcd1a6 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayProxyOnlyService.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayProxyOnlyService.kt @@ -27,7 +27,7 @@ class V2RayProxyOnlyService : Service(), ServiceControl { * @return The start mode. */ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - V2RayServiceManager.startV2rayPoint() + V2RayServiceManager.startCoreLoop() return START_STICKY } @@ -36,7 +36,7 @@ class V2RayProxyOnlyService : Service(), ServiceControl { */ override fun onDestroy() { super.onDestroy() - V2RayServiceManager.stopV2rayPoint() + V2RayServiceManager.stopCoreLoop() } /** diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt index 7ef0030b..de02ac6e 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt @@ -24,14 +24,14 @@ import go.Seq import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import libv2ray.CoreCallbackHandler +import libv2ray.CoreController import libv2ray.Libv2ray -import libv2ray.V2RayPoint -import libv2ray.V2RayVPNServiceSupportsSet import java.lang.ref.SoftReference object V2RayServiceManager { - private val v2rayPoint: V2RayPoint = Libv2ray.newV2RayPoint(V2RayCallback(), Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) + private val coreController: CoreController = Libv2ray.newCoreController(CoreCallback()) private val mMsgReceive = ReceiveMessageHandler() private var currentConfig: ProfileItem? = null @@ -39,7 +39,7 @@ object V2RayServiceManager { set(value) { field = value Seq.setContext(value?.get()?.getService()?.applicationContext) - Libv2ray.initV2Env(Utils.userAssetPath(value?.get()?.getService()), Utils.getDeviceIdForXUDPBaseKey()) + Libv2ray.initCoreEnv(Utils.userAssetPath(value?.get()?.getService()), Utils.getDeviceIdForXUDPBaseKey()) } /** @@ -81,7 +81,7 @@ object V2RayServiceManager { * Checks if the V2Ray service is running. * @return True if the service is running, false otherwise. */ - fun isRunning() = v2rayPoint.isRunning + fun isRunning() = coreController.isRunning /** * Gets the name of the currently running server. @@ -91,10 +91,11 @@ object V2RayServiceManager { /** * Starts the context service for V2Ray. + * Chooses between VPN service or Proxy-only service based on user settings. * @param context The context from which the service is started. */ private fun startContextService(context: Context) { - if (v2rayPoint.isRunning) return + if (coreController.isRunning) return val guid = MmkvManager.getSelectServer() ?: return val config = MmkvManager.decodeServerConfig(guid) ?: return if (config.configType != EConfigType.CUSTOM @@ -124,18 +125,18 @@ object V2RayServiceManager { /** * Refer to the official documentation for [registerReceiver](https://developer.android.com/reference/androidx/core/content/ContextCompat#registerReceiver(android.content.Context,android.content.BroadcastReceiver,android.content.IntentFilter,int): * `registerReceiver(Context, BroadcastReceiver, IntentFilter, int)`. - * Starts the V2Ray point. + * Starts the V2Ray core service. */ - fun startV2rayPoint() { - val service = getService() ?: return - val guid = MmkvManager.getSelectServer() ?: return - val config = MmkvManager.decodeServerConfig(guid) ?: return - if (v2rayPoint.isRunning) { - return + fun startCoreLoop(): Boolean { + val service = getService() ?: return false + val guid = MmkvManager.getSelectServer() ?: return false + val config = MmkvManager.decodeServerConfig(guid) ?: return false + if (coreController.isRunning) { + return false } val result = V2rayConfigManager.getV2rayConfig(service, guid) if (!result.status) - return + return false try { val mFilter = IntentFilter(AppConfig.BROADCAST_ACTION_SERVICE) @@ -145,39 +146,49 @@ object V2RayServiceManager { ContextCompat.registerReceiver(service, mMsgReceive, mFilter, Utils.receiverFlags()) } catch (e: Exception) { Log.e(AppConfig.TAG, "Failed to register broadcast receiver", e) + return false } - v2rayPoint.configureFileContent = result.content - v2rayPoint.domainName = result.domainPort currentConfig = config try { - v2rayPoint.runLoop(MmkvManager.decodeSettingsBool(AppConfig.PREF_PREFER_IPV6)) + coreController.startLoop(result.content) } catch (e: Exception) { - Log.e(AppConfig.TAG, "Failed to start V2Ray loop", e) + Log.e(AppConfig.TAG, "Failed to start Core loop", e) + return false } - if (v2rayPoint.isRunning) { + if (coreController.isRunning == false) { + MessageUtil.sendMsg2UI(service, AppConfig.MSG_STATE_START_FAILURE, "") + NotificationService.cancelNotification() + return false + } + + try { MessageUtil.sendMsg2UI(service, AppConfig.MSG_STATE_START_SUCCESS, "") + NotificationService.startSpeedNotification(currentConfig) NotificationService.showNotification(currentConfig) PluginUtil.runPlugin(service, config, result.domainPort) - } else { - MessageUtil.sendMsg2UI(service, AppConfig.MSG_STATE_START_FAILURE, "") - NotificationService.cancelNotification() + } catch (e: Exception) { + Log.e(AppConfig.TAG, "Failed to startup service", e) + return false } + return true } /** - * Stops the V2Ray point. + * Stops the V2Ray core service. + * Unregisters broadcast receivers, stops notifications, and shuts down plugins. + * @return True if the core was stopped successfully, false otherwise. */ - fun stopV2rayPoint() { - val service = getService() ?: return + fun stopCoreLoop(): Boolean { + val service = getService() ?: return false - if (v2rayPoint.isRunning) { + if (coreController.isRunning) { CoroutineScope(Dispatchers.IO).launch { try { - v2rayPoint.stopLoop() + coreController.stopLoop() } catch (e: Exception) { Log.e(AppConfig.TAG, "Failed to stop V2Ray loop", e) } @@ -193,6 +204,8 @@ object V2RayServiceManager { Log.e(AppConfig.TAG, "Failed to unregister broadcast receiver", e) } PluginUtil.stopPlugin() + + return true } /** @@ -202,27 +215,29 @@ object V2RayServiceManager { * @return The statistics value. */ fun queryStats(tag: String, link: String): Long { - return v2rayPoint.queryStats(tag, link) + return coreController.queryStats(tag, link) } /** - * Measures the delay for V2Ray. + * Measures the connection delay for the current V2Ray configuration. + * Tests with primary URL first, then falls back to alternative URL if needed. + * Also fetches remote IP information if the delay test was successful. */ private fun measureV2rayDelay() { CoroutineScope(Dispatchers.IO).launch { val service = getService() ?: return@launch var time = -1L var errstr = "" - if (v2rayPoint.isRunning) { + if (coreController.isRunning) { try { - time = v2rayPoint.measureDelay(SettingsManager.getDelayTestUrl()) + time = coreController.measureDelay(SettingsManager.getDelayTestUrl()) } catch (e: Exception) { Log.e(AppConfig.TAG, "Failed to measure delay with primary URL", e) errstr = e.message?.substringAfter("\":") ?: "empty message" } if (time == -1L) { try { - time = v2rayPoint.measureDelay(SettingsManager.getDelayTestUrl(true)) + time = coreController.measureDelay(SettingsManager.getDelayTestUrl(true)) } catch (e: Exception) { Log.e(AppConfig.TAG, "Failed to measure delay with alternative URL", e) errstr = e.message?.substringAfter("\":") ?: "empty message" @@ -253,10 +268,25 @@ object V2RayServiceManager { return serviceControl?.get()?.getService() } - private class V2RayCallback : V2RayVPNServiceSupportsSet { + /** + * Core callback handler implementation for handling V2Ray core events. + * Handles startup, shutdown, socket protection, and status emission. + */ + private class CoreCallback : CoreCallbackHandler { + /** + * Called when V2Ray core starts up. + * @return 0 for success, any other value for failure. + */ + override fun startup(): Long { + return 0 + } + + /** + * Called when V2Ray core shuts down. + * @return 0 for success, any other value for failure. + */ override fun shutdown(): Long { val serviceControl = serviceControl?.get() ?: return -1 - // called by go return try { serviceControl.stopService() 0 @@ -266,46 +296,35 @@ object V2RayServiceManager { } } - override fun prepare(): Long { - return 0 - } - + /** + * Protects a socket from being routed through the VPN. + * @param l The socket file descriptor. + * @return True if protection was successful, false otherwise. + */ override fun protect(l: Long): Boolean { val serviceControl = serviceControl?.get() ?: return true return serviceControl.vpnProtect(l.toInt()) } /** - * Called by Go to emit status. - * @param l The status code. - * @param s The status message. - * @return The status code. + * Called when V2Ray core emits status information. + * @param l Status code. + * @param s Status message. + * @return Always returns 0. */ override fun onEmitStatus(l: Long, s: String?): Long { return 0 } - - /** - * Called by Go to set up the service. - * @param s The setup string. - * @return The status code. - */ - override fun setup(s: String): Long { - val serviceControl = serviceControl?.get() ?: return -1 - return try { - serviceControl.startService() - NotificationService.startSpeedNotification(currentConfig) - 0 - } catch (e: Exception) { - Log.e(AppConfig.TAG, "Failed to setup service in callback", e) - -1 - } - } } + /** + * Broadcast receiver for handling messages sent to the service. + * Handles registration, service control, and screen events. + */ private class ReceiveMessageHandler : BroadcastReceiver() { /** * Handles received broadcast messages. + * Processes service control messages and screen state changes. * @param ctx The context in which the receiver is running. * @param intent The intent being received. */ @@ -313,7 +332,7 @@ object V2RayServiceManager { val serviceControl = serviceControl?.get() ?: return when (intent?.getIntExtra("key", 0)) { AppConfig.MSG_REGISTER_CLIENT -> { - if (v2rayPoint.isRunning) { + if (coreController.isRunning) { MessageUtil.sendMsg2UI(serviceControl.getService(), AppConfig.MSG_STATE_RUNNING, "") } else { MessageUtil.sendMsg2UI(serviceControl.getService(), AppConfig.MSG_STATE_NOT_RUNNING, "") diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayTestService.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayTestService.kt index 180dc24b..3fef1ae1 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayTestService.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayTestService.kt @@ -32,7 +32,7 @@ class V2RayTestService : Service() { override fun onCreate() { super.onCreate() Seq.setContext(this) - Libv2ray.initV2Env(Utils.userAssetPath(this), Utils.getDeviceIdForXUDPBaseKey()) + Libv2ray.initCoreEnv(Utils.userAssetPath(this), Utils.getDeviceIdForXUDPBaseKey()) } /** diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayVpnService.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayVpnService.kt index 5a3b67f5..9fc24d56 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayVpnService.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayVpnService.kt @@ -104,7 +104,9 @@ class V2RayVpnService : VpnService(), ServiceControl { } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - V2RayServiceManager.startV2rayPoint() + if (V2RayServiceManager.startCoreLoop()) { + startService() + } return START_STICKY //return super.onStartCommand(intent, flags, startId) } @@ -255,6 +257,7 @@ class V2RayVpnService : VpnService(), ServiceControl { * Starts the tun2socks process with the appropriate parameters. */ private fun runTun2socks() { + Log.i(AppConfig.TAG, "Start run $TUN2SOCKS") val socksPort = SettingsManager.getSocksPort() val cmd = arrayListOf( File(applicationContext.applicationInfo.nativeLibraryDir, TUN2SOCKS).absolutePath, @@ -293,11 +296,11 @@ class V2RayVpnService : VpnService(), ServiceControl { runTun2socks() } }.start() - Log.i(AppConfig.TAG, process.toString()) + Log.i(AppConfig.TAG, "$TUN2SOCKS process info : ${process.toString()}") sendFd() } catch (e: Exception) { - Log.e(AppConfig.TAG, "Failed to start tun2socks process", e) + Log.e(AppConfig.TAG, "Failed to start $TUN2SOCKS process", e) } } @@ -308,13 +311,13 @@ class V2RayVpnService : VpnService(), ServiceControl { private fun sendFd() { val fd = mInterface.fileDescriptor val path = File(applicationContext.filesDir, "sock_path").absolutePath - Log.i(AppConfig.TAG, path) + Log.i(AppConfig.TAG, "LocalSocket path : $path") CoroutineScope(Dispatchers.IO).launch { var tries = 0 while (true) try { Thread.sleep(50L shl tries) - Log.i(AppConfig.TAG, "sendFd tries: $tries") + Log.i(AppConfig.TAG, "LocalSocket sendFd tries: $tries") LocalSocket().use { localSocket -> localSocket.connect(LocalSocketAddress(path, LocalSocketAddress.Namespace.FILESYSTEM)) localSocket.setFileDescriptorsForSend(arrayOf(fd)) @@ -348,13 +351,13 @@ class V2RayVpnService : VpnService(), ServiceControl { } try { - Log.i(AppConfig.TAG, "tun2socks destroy") + Log.i(AppConfig.TAG, "$TUN2SOCKS destroy") process.destroy() } catch (e: Exception) { - Log.e(AppConfig.TAG, "Failed to destroy tun2socks process", e) + Log.e(AppConfig.TAG, "Failed to destroy $TUN2SOCKS process", e) } - V2RayServiceManager.stopV2rayPoint() + V2RayServiceManager.stopCoreLoop() if (isForced) { //stopSelf has to be called ahead of mInterface.close(). otherwise v2ray core cannot be stooped From 83fd6efc170e991b3af92e62c422e48f61b630b1 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Thu, 17 Apr 2025 14:08:45 +0800 Subject: [PATCH 11/81] Resolve remote host names in the configuration file to IP addresses --- .../main/java/com/v2ray/ang/fmt/FmtBase.kt | 7 ++++ .../main/java/com/v2ray/ang/fmt/HttpFmt.kt | 2 +- .../java/com/v2ray/ang/fmt/ShadowsocksFmt.kt | 2 +- .../main/java/com/v2ray/ang/fmt/SocksFmt.kt | 2 +- .../main/java/com/v2ray/ang/fmt/TrojanFmt.kt | 2 +- .../main/java/com/v2ray/ang/fmt/VlessFmt.kt | 2 +- .../main/java/com/v2ray/ang/fmt/VmessFmt.kt | 2 +- .../java/com/v2ray/ang/fmt/WireguardFmt.kt | 2 +- .../com/v2ray/ang/ui/MainRecyclerAdapter.kt | 14 +++++-- .../main/java/com/v2ray/ang/util/HttpUtil.kt | 40 ++++++++++++++++++- 10 files changed, 63 insertions(+), 12 deletions(-) diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt index 407e5c37..f8bc82fb 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt @@ -4,6 +4,8 @@ import com.v2ray.ang.AppConfig import com.v2ray.ang.dto.NetworkType import com.v2ray.ang.dto.ProfileItem import com.v2ray.ang.extension.isNotNullEmpty +import com.v2ray.ang.handler.MmkvManager +import com.v2ray.ang.util.HttpUtil import com.v2ray.ang.util.Utils import java.net.URI @@ -148,4 +150,9 @@ open class FmtBase { return dicQuery } + + fun resolveHostToIP(server: String?): String { + return HttpUtil.resolveHostToIP(server.orEmpty(), MmkvManager.decodeSettingsBool(AppConfig.PREF_PREFER_IPV6) == true) + } + } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/HttpFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/HttpFmt.kt index faac0230..2ff28d8c 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/HttpFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/HttpFmt.kt @@ -16,7 +16,7 @@ object HttpFmt : FmtBase() { val outboundBean = OutboundBean.create(EConfigType.HTTP) outboundBean?.settings?.servers?.first()?.let { server -> - server.address = profileItem.server.orEmpty() + server.address = resolveHostToIP(profileItem.server) server.port = profileItem.serverPort.orEmpty().toInt() if (profileItem.username.isNotNullEmpty()) { val socksUsersBean = OutboundBean.OutSettingsBean.ServersBean.SocksUsersBean() diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/ShadowsocksFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/ShadowsocksFmt.kt index fb383d25..69588c2c 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/ShadowsocksFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/ShadowsocksFmt.kt @@ -134,7 +134,7 @@ object ShadowsocksFmt : FmtBase() { val outboundBean = OutboundBean.create(EConfigType.SHADOWSOCKS) outboundBean?.settings?.servers?.first()?.let { server -> - server.address = profileItem.server.orEmpty() + server.address = resolveHostToIP(profileItem.server) server.port = profileItem.serverPort.orEmpty().toInt() server.password = profileItem.password server.method = profileItem.method diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt index f37030c2..fad0bb4c 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt @@ -63,7 +63,7 @@ object SocksFmt : FmtBase() { val outboundBean = OutboundBean.create(EConfigType.SOCKS) outboundBean?.settings?.servers?.first()?.let { server -> - server.address = profileItem.server.orEmpty() + server.address = resolveHostToIP(profileItem.server) server.port = profileItem.serverPort.orEmpty().toInt() if (profileItem.username.isNotNullEmpty()) { val socksUsersBean = OutboundBean.OutSettingsBean.ServersBean.SocksUsersBean() diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt index 56883cd8..7ed00a21 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt @@ -63,7 +63,7 @@ object TrojanFmt : FmtBase() { val outboundBean = OutboundBean.create(EConfigType.TROJAN) outboundBean?.settings?.servers?.first()?.let { server -> - server.address = profileItem.server.orEmpty() + server.address = resolveHostToIP(profileItem.server) server.port = profileItem.serverPort.orEmpty().toInt() server.password = profileItem.password server.flow = profileItem.flow diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VlessFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VlessFmt.kt index 77d2f2a3..d6df582c 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VlessFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VlessFmt.kt @@ -60,7 +60,7 @@ object VlessFmt : FmtBase() { val outboundBean = OutboundBean.create(EConfigType.VLESS) outboundBean?.settings?.vnext?.first()?.let { vnext -> - vnext.address = profileItem.server.orEmpty() + vnext.address = resolveHostToIP(profileItem.server) vnext.port = profileItem.serverPort.orEmpty().toInt() vnext.users[0].id = profileItem.password.orEmpty() vnext.users[0].encryption = profileItem.method diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt index bbc913d3..eac159eb 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt @@ -171,7 +171,7 @@ object VmessFmt : FmtBase() { val outboundBean = OutboundBean.create(EConfigType.VMESS) outboundBean?.settings?.vnext?.first()?.let { vnext -> - vnext.address = profileItem.server.orEmpty() + vnext.address = resolveHostToIP(profileItem.server) vnext.port = profileItem.serverPort.orEmpty().toInt() vnext.users[0].id = profileItem.password.orEmpty() vnext.users[0].security = profileItem.method diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt index e2bc5f90..44ce1a04 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt @@ -113,7 +113,7 @@ object WireguardFmt : FmtBase() { wireguard.peers?.firstOrNull()?.let { peer -> peer.publicKey = profileItem.publicKey.orEmpty() peer.preSharedKey = profileItem.preSharedKey?.takeIf { it.isNotEmpty() } - peer.endpoint = Utils.getIpv6Address(profileItem.server) + ":${profileItem.serverPort}" + peer.endpoint = Utils.getIpv6Address(resolveHostToIP(profileItem.server)) + ":${profileItem.serverPort}" } wireguard.mtu = profileItem.mtu wireguard.reserved = profileItem.reserved?.takeIf { it.isNotBlank() }?.split(",")?.filter { it.isNotBlank() }?.map { it.trim().toInt() } 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 179f70ae..e7ea6211 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 @@ -27,6 +27,7 @@ import com.v2ray.ang.handler.MmkvManager import com.v2ray.ang.helper.ItemTouchHelperAdapter import com.v2ray.ang.helper.ItemTouchHelperViewHolder import com.v2ray.ang.service.V2RayServiceManager +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -220,10 +221,15 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter Date: Thu, 17 Apr 2025 15:23:57 +0800 Subject: [PATCH 12/81] Refactor the Outbound transport and tls in the configuration file --- .../java/com/v2ray/ang/dto/V2rayConfig.kt | 144 ----------------- .../main/java/com/v2ray/ang/fmt/FmtBase.kt | 151 ++++++++++++++++++ .../java/com/v2ray/ang/fmt/ShadowsocksFmt.kt | 52 +++--- .../main/java/com/v2ray/ang/fmt/TrojanFmt.kt | 52 +++--- .../main/java/com/v2ray/ang/fmt/VlessFmt.kt | 55 ++++--- .../main/java/com/v2ray/ang/fmt/VmessFmt.kt | 52 +++--- .../v2ray/ang/handler/V2rayConfigManager.kt | 7 +- 7 files changed, 274 insertions(+), 239 deletions(-) diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt index a3323d87..00de185d 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt @@ -349,139 +349,6 @@ data class V2rayConfig( ) } - fun populateTransportSettings( - transport: String, - headerType: String?, - host: String?, - path: String?, - seed: String?, - quicSecurity: String?, - key: String?, - mode: String?, - serviceName: String?, - authority: String? - ): String? { - var sni: String? = null - network = if (transport.isEmpty()) NetworkType.TCP.type else transport - when (network) { - NetworkType.TCP.type -> { - val tcpSetting = TcpSettingsBean() - if (headerType == AppConfig.HEADER_TYPE_HTTP) { - tcpSetting.header.type = AppConfig.HEADER_TYPE_HTTP - if (!TextUtils.isEmpty(host) || !TextUtils.isEmpty(path)) { - val requestObj = TcpSettingsBean.HeaderBean.RequestBean() - requestObj.headers.Host = host.orEmpty().split(",").map { it.trim() }.filter { it.isNotEmpty() } - requestObj.path = path.orEmpty().split(",").map { it.trim() }.filter { it.isNotEmpty() } - tcpSetting.header.request = requestObj - sni = requestObj.headers.Host?.getOrNull(0) - } - } else { - tcpSetting.header.type = "none" - sni = host - } - tcpSettings = tcpSetting - } - - NetworkType.KCP.type -> { - val kcpsetting = KcpSettingsBean() - kcpsetting.header.type = headerType ?: "none" - if (seed.isNullOrEmpty()) { - kcpsetting.seed = null - } else { - kcpsetting.seed = seed - } - if (host.isNullOrEmpty()) { - kcpsetting.header.domain = null - } else { - kcpsetting.header.domain = host - } - kcpSettings = kcpsetting - } - - NetworkType.WS.type -> { - val wssetting = WsSettingsBean() - wssetting.headers.Host = host.orEmpty() - sni = host - wssetting.path = path ?: "/" - wsSettings = wssetting - } - - NetworkType.HTTP_UPGRADE.type -> { - val httpupgradeSetting = HttpupgradeSettingsBean() - httpupgradeSetting.host = host.orEmpty() - sni = host - httpupgradeSetting.path = path ?: "/" - httpupgradeSettings = httpupgradeSetting - } - - NetworkType.XHTTP.type -> { - val xhttpSetting = XhttpSettingsBean() - xhttpSetting.host = host.orEmpty() - sni = host - xhttpSetting.path = path ?: "/" - xhttpSettings = xhttpSetting - } - - NetworkType.H2.type, NetworkType.HTTP.type -> { - network = NetworkType.H2.type - val h2Setting = HttpSettingsBean() - h2Setting.host = host.orEmpty().split(",").map { it.trim() }.filter { it.isNotEmpty() } - sni = h2Setting.host.getOrNull(0) - h2Setting.path = path ?: "/" - httpSettings = h2Setting - } - -// "quic" -> { -// val quicsetting = QuicSettingBean() -// quicsetting.security = quicSecurity ?: "none" -// quicsetting.key = key.orEmpty() -// quicsetting.header.type = headerType ?: "none" -// quicSettings = quicsetting -// } - - NetworkType.GRPC.type -> { - val grpcSetting = GrpcSettingsBean() - grpcSetting.multiMode = mode == "multi" - grpcSetting.serviceName = serviceName.orEmpty() - grpcSetting.authority = authority.orEmpty() - grpcSetting.idle_timeout = 60 - grpcSetting.health_check_timeout = 20 - sni = authority - grpcSettings = grpcSetting - } - } - return sni - } - - fun populateTlsSettings( - streamSecurity: String, - allowInsecure: Boolean, - sni: String?, - fingerprint: String?, - alpns: String?, - publicKey: String?, - shortId: String?, - spiderX: String? - ) { - security = if (streamSecurity.isEmpty()) null else streamSecurity - if (security == null) return - val tlsSetting = TlsSettingsBean( - allowInsecure = allowInsecure, - serverName = if (sni.isNullOrEmpty()) null else sni, - fingerprint = if (fingerprint.isNullOrEmpty()) null else fingerprint, - alpn = if (alpns.isNullOrEmpty()) null else alpns.split(",").map { it.trim() }.filter { it.isNotEmpty() }, - publicKey = if (publicKey.isNullOrEmpty()) null else publicKey, - shortId = if (shortId.isNullOrEmpty()) null else shortId, - spiderX = if (spiderX.isNullOrEmpty()) null else spiderX, - ) - if (security == AppConfig.TLS) { - tlsSettings = tlsSetting - realitySettings = null - } else if (security == AppConfig.REALITY) { - tlsSettings = null - realitySettings = tlsSetting - } - } } data class MuxBean( @@ -723,15 +590,4 @@ data class V2rayConfig( return null } - fun toPrettyPrinting(): String { - return GsonBuilder() - .setPrettyPrinting() - .disableHtmlEscaping() - .registerTypeAdapter( // custom serialiser is needed here since JSON by default parse number as Double, core will fail to start - object : TypeToken() {}.type, - JsonSerializer { src: Double?, _: Type?, _: JsonSerializationContext? -> JsonPrimitive(src?.toInt()) } - ) - .create() - .toJson(this) - } } \ No newline at end of file diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt index f8bc82fb..bc3229fe 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt @@ -1,11 +1,22 @@ package com.v2ray.ang.fmt +import android.text.TextUtils import com.v2ray.ang.AppConfig import com.v2ray.ang.dto.NetworkType import com.v2ray.ang.dto.ProfileItem +import com.v2ray.ang.dto.V2rayConfig.OutboundBean.StreamSettingsBean +import com.v2ray.ang.dto.V2rayConfig.OutboundBean.StreamSettingsBean.GrpcSettingsBean +import com.v2ray.ang.dto.V2rayConfig.OutboundBean.StreamSettingsBean.HttpSettingsBean +import com.v2ray.ang.dto.V2rayConfig.OutboundBean.StreamSettingsBean.HttpupgradeSettingsBean +import com.v2ray.ang.dto.V2rayConfig.OutboundBean.StreamSettingsBean.KcpSettingsBean +import com.v2ray.ang.dto.V2rayConfig.OutboundBean.StreamSettingsBean.TcpSettingsBean +import com.v2ray.ang.dto.V2rayConfig.OutboundBean.StreamSettingsBean.TlsSettingsBean +import com.v2ray.ang.dto.V2rayConfig.OutboundBean.StreamSettingsBean.WsSettingsBean +import com.v2ray.ang.dto.V2rayConfig.OutboundBean.StreamSettingsBean.XhttpSettingsBean import com.v2ray.ang.extension.isNotNullEmpty import com.v2ray.ang.handler.MmkvManager import com.v2ray.ang.util.HttpUtil +import com.v2ray.ang.util.JsonUtil import com.v2ray.ang.util.Utils import java.net.URI @@ -151,6 +162,146 @@ open class FmtBase { return dicQuery } + fun populateTransportSettings( + streamSettings: StreamSettingsBean, + transport: String, + headerType: String?, + host: String?, + path: String?, + seed: String?, + quicSecurity: String?, + key: String?, + mode: String?, + serviceName: String?, + authority: String?, + xhttpMode: String?, + xhttpExtra: String?, + ): String? { + var sni: String? = null + streamSettings.network = if (transport.isEmpty()) NetworkType.TCP.type else transport + when (streamSettings.network) { + NetworkType.TCP.type -> { + val tcpSetting = TcpSettingsBean() + if (headerType == AppConfig.HEADER_TYPE_HTTP) { + tcpSetting.header.type = AppConfig.HEADER_TYPE_HTTP + if (!TextUtils.isEmpty(host) || !TextUtils.isEmpty(path)) { + val requestObj = TcpSettingsBean.HeaderBean.RequestBean() + requestObj.headers.Host = host.orEmpty().split(",").map { it.trim() }.filter { it.isNotEmpty() } + requestObj.path = path.orEmpty().split(",").map { it.trim() }.filter { it.isNotEmpty() } + tcpSetting.header.request = requestObj + sni = requestObj.headers.Host?.getOrNull(0) + } + } else { + tcpSetting.header.type = "none" + sni = host + } + streamSettings.tcpSettings = tcpSetting + } + + NetworkType.KCP.type -> { + val kcpsetting = KcpSettingsBean() + kcpsetting.header.type = headerType ?: "none" + if (seed.isNullOrEmpty()) { + kcpsetting.seed = null + } else { + kcpsetting.seed = seed + } + if (host.isNullOrEmpty()) { + kcpsetting.header.domain = null + } else { + kcpsetting.header.domain = host + } + streamSettings.kcpSettings = kcpsetting + } + + NetworkType.WS.type -> { + val wssetting = WsSettingsBean() + wssetting.headers.Host = host.orEmpty() + sni = host + wssetting.path = path ?: "/" + streamSettings.wsSettings = wssetting + } + + NetworkType.HTTP_UPGRADE.type -> { + val httpupgradeSetting = HttpupgradeSettingsBean() + httpupgradeSetting.host = host.orEmpty() + sni = host + httpupgradeSetting.path = path ?: "/" + streamSettings.httpupgradeSettings = httpupgradeSetting + } + + NetworkType.XHTTP.type -> { + val xhttpSetting = XhttpSettingsBean() + xhttpSetting.host = host.orEmpty() + sni = host + xhttpSetting.path = path ?: "/" + xhttpSetting.mode = xhttpMode + xhttpSetting.extra = JsonUtil.parseString(xhttpExtra) + streamSettings.xhttpSettings = xhttpSetting + } + + NetworkType.H2.type, NetworkType.HTTP.type -> { + streamSettings.network = NetworkType.H2.type + val h2Setting = HttpSettingsBean() + h2Setting.host = host.orEmpty().split(",").map { it.trim() }.filter { it.isNotEmpty() } + sni = h2Setting.host.getOrNull(0) + h2Setting.path = path ?: "/" + streamSettings.httpSettings = h2Setting + } + +// "quic" -> { +// val quicsetting = QuicSettingBean() +// quicsetting.security = quicSecurity ?: "none" +// quicsetting.key = key.orEmpty() +// quicsetting.header.type = headerType ?: "none" +// quicSettings = quicsetting +// } + + NetworkType.GRPC.type -> { + val grpcSetting = GrpcSettingsBean() + grpcSetting.multiMode = mode == "multi" + grpcSetting.serviceName = serviceName.orEmpty() + grpcSetting.authority = authority.orEmpty() + grpcSetting.idle_timeout = 60 + grpcSetting.health_check_timeout = 20 + sni = authority + streamSettings.grpcSettings = grpcSetting + } + } + return sni + } + + fun populateTlsSettings( + streamSettings: StreamSettingsBean, + streamSecurity: String, + allowInsecure: Boolean, + sni: String?, + fingerprint: String?, + alpns: String?, + publicKey: String?, + shortId: String?, + spiderX: String? + ) { + streamSettings.security = if (streamSecurity.isEmpty()) null else streamSecurity + if (streamSettings.security == null) return + val tlsSetting = TlsSettingsBean( + allowInsecure = allowInsecure, + serverName = if (sni.isNullOrEmpty()) null else sni, + fingerprint = if (fingerprint.isNullOrEmpty()) null else fingerprint, + alpn = if (alpns.isNullOrEmpty()) null else alpns.split(",").map { it.trim() }.filter { it.isNotEmpty() }, + publicKey = if (publicKey.isNullOrEmpty()) null else publicKey, + shortId = if (shortId.isNullOrEmpty()) null else shortId, + spiderX = if (spiderX.isNullOrEmpty()) null else spiderX, + ) + if (streamSettings.security == AppConfig.TLS) { + streamSettings.tlsSettings = tlsSetting + streamSettings.realitySettings = null + } else if (streamSettings.security == AppConfig.REALITY) { + streamSettings.tlsSettings = null + streamSettings.realitySettings = tlsSetting + } + } + fun resolveHostToIP(server: String?): String { return HttpUtil.resolveHostToIP(server.orEmpty(), MmkvManager.decodeSettingsBool(AppConfig.PREF_PREFER_IPV6) == true) } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/ShadowsocksFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/ShadowsocksFmt.kt index 69588c2c..d0acc28d 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/ShadowsocksFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/ShadowsocksFmt.kt @@ -140,29 +140,37 @@ object ShadowsocksFmt : FmtBase() { server.method = profileItem.method } - val sni = outboundBean?.streamSettings?.populateTransportSettings( - profileItem.network.orEmpty(), - profileItem.headerType, - profileItem.host, - profileItem.path, - profileItem.seed, - profileItem.quicSecurity, - profileItem.quicKey, - profileItem.mode, - profileItem.serviceName, - profileItem.authority, - ) + val sni = outboundBean?.streamSettings?.let { + populateTransportSettings( + it, + profileItem.network.orEmpty(), + profileItem.headerType, + profileItem.host, + profileItem.path, + profileItem.seed, + profileItem.quicSecurity, + profileItem.quicKey, + profileItem.mode, + profileItem.serviceName, + profileItem.authority, + profileItem.xhttpMode, + profileItem.xhttpExtra + ) + } - outboundBean?.streamSettings?.populateTlsSettings( - profileItem.security.orEmpty(), - profileItem.insecure == true, - if (profileItem.sni.isNullOrEmpty()) sni else profileItem.sni, - profileItem.fingerPrint, - profileItem.alpn, - profileItem.publicKey, - profileItem.shortId, - profileItem.spiderX, - ) + outboundBean?.streamSettings?.let { + populateTlsSettings( + it, + profileItem.security.orEmpty(), + profileItem.insecure == true, + if (profileItem.sni.isNullOrEmpty()) sni else profileItem.sni, + profileItem.fingerPrint, + profileItem.alpn, + profileItem.publicKey, + profileItem.shortId, + profileItem.spiderX, + ) + } return outboundBean } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt index 7ed00a21..48dd8f4e 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt @@ -69,29 +69,37 @@ object TrojanFmt : FmtBase() { server.flow = profileItem.flow } - val sni = outboundBean?.streamSettings?.populateTransportSettings( - profileItem.network.orEmpty(), - profileItem.headerType, - profileItem.host, - profileItem.path, - profileItem.seed, - profileItem.quicSecurity, - profileItem.quicKey, - profileItem.mode, - profileItem.serviceName, - profileItem.authority, - ) + val sni = outboundBean?.streamSettings?.let { + populateTransportSettings( + it, + profileItem.network.orEmpty(), + profileItem.headerType, + profileItem.host, + profileItem.path, + profileItem.seed, + profileItem.quicSecurity, + profileItem.quicKey, + profileItem.mode, + profileItem.serviceName, + profileItem.authority, + profileItem.xhttpMode, + profileItem.xhttpExtra + ) + } - outboundBean?.streamSettings?.populateTlsSettings( - profileItem.security.orEmpty(), - profileItem.insecure == true, - if (profileItem.sni.isNullOrEmpty()) sni else profileItem.sni, - profileItem.fingerPrint, - profileItem.alpn, - profileItem.publicKey, - profileItem.shortId, - profileItem.spiderX, - ) + outboundBean?.streamSettings?.let { + populateTlsSettings( + it, + profileItem.security.orEmpty(), + profileItem.insecure == true, + if (profileItem.sni.isNullOrEmpty()) sni else profileItem.sni, + profileItem.fingerPrint, + profileItem.alpn, + profileItem.publicKey, + profileItem.shortId, + profileItem.spiderX, + ) + } return outboundBean } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VlessFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VlessFmt.kt index d6df582c..1438cd7e 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VlessFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VlessFmt.kt @@ -6,7 +6,6 @@ import com.v2ray.ang.dto.ProfileItem import com.v2ray.ang.dto.V2rayConfig.OutboundBean import com.v2ray.ang.extension.idnHost import com.v2ray.ang.handler.MmkvManager -import com.v2ray.ang.util.JsonUtil import com.v2ray.ang.util.Utils import java.net.URI @@ -67,31 +66,37 @@ object VlessFmt : FmtBase() { vnext.users[0].flow = profileItem.flow } - val sni = outboundBean?.streamSettings?.populateTransportSettings( - profileItem.network.orEmpty(), - profileItem.headerType, - profileItem.host, - profileItem.path, - profileItem.seed, - profileItem.quicSecurity, - profileItem.quicKey, - profileItem.mode, - profileItem.serviceName, - profileItem.authority, - ) - outboundBean?.streamSettings?.xhttpSettings?.mode = profileItem.xhttpMode - outboundBean?.streamSettings?.xhttpSettings?.extra = JsonUtil.parseString(profileItem.xhttpExtra) + val sni = outboundBean?.streamSettings?.let { + populateTransportSettings( + it, + profileItem.network.orEmpty(), + profileItem.headerType, + profileItem.host, + profileItem.path, + profileItem.seed, + profileItem.quicSecurity, + profileItem.quicKey, + profileItem.mode, + profileItem.serviceName, + profileItem.authority, + profileItem.xhttpMode, + profileItem.xhttpExtra + ) + } - outboundBean?.streamSettings?.populateTlsSettings( - profileItem.security.orEmpty(), - profileItem.insecure == true, - if (profileItem.sni.isNullOrEmpty()) sni else profileItem.sni, - profileItem.fingerPrint, - profileItem.alpn, - profileItem.publicKey, - profileItem.shortId, - profileItem.spiderX, - ) + outboundBean?.streamSettings?.let { + populateTlsSettings( + it, + profileItem.security.orEmpty(), + profileItem.insecure == true, + if (profileItem.sni.isNullOrEmpty()) sni else profileItem.sni, + profileItem.fingerPrint, + profileItem.alpn, + profileItem.publicKey, + profileItem.shortId, + profileItem.spiderX, + ) + } return outboundBean } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt index eac159eb..cb157e1e 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt @@ -177,29 +177,37 @@ object VmessFmt : FmtBase() { vnext.users[0].security = profileItem.method } - val sni = outboundBean?.streamSettings?.populateTransportSettings( - profileItem.network.orEmpty(), - profileItem.headerType, - profileItem.host, - profileItem.path, - profileItem.seed, - profileItem.quicSecurity, - profileItem.quicKey, - profileItem.mode, - profileItem.serviceName, - profileItem.authority, - ) + val sni = outboundBean?.streamSettings?.let { + populateTransportSettings( + it, + profileItem.network.orEmpty(), + profileItem.headerType, + profileItem.host, + profileItem.path, + profileItem.seed, + profileItem.quicSecurity, + profileItem.quicKey, + profileItem.mode, + profileItem.serviceName, + profileItem.authority, + profileItem.xhttpMode, + profileItem.xhttpExtra + ) + } - outboundBean?.streamSettings?.populateTlsSettings( - profileItem.security.orEmpty(), - profileItem.insecure == true, - if (profileItem.sni.isNullOrEmpty()) sni else profileItem.sni, - profileItem.fingerPrint, - profileItem.alpn, - null, - null, - null - ) + outboundBean?.streamSettings?.let { + populateTlsSettings( + it, + profileItem.security.orEmpty(), + profileItem.insecure == true, + if (profileItem.sni.isNullOrEmpty()) sni else profileItem.sni, + profileItem.fingerPrint, + profileItem.alpn, + null, + null, + null + ) + } return outboundBean } 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 ffd40eda..51c66523 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 @@ -151,7 +151,7 @@ object V2rayConfigManager { } result.status = true - result.content = v2rayConfig.toPrettyPrinting() + result.content = JsonUtil.toJsonPretty(v2rayConfig) ?: "" result.domainPort = if (retMore.first) retMore.second else retOut.second result.guid = guid return result @@ -195,7 +195,7 @@ object V2rayConfigManager { } result.status = true - result.content = v2rayConfig.toPrettyPrinting() + result.content = JsonUtil.toJsonPretty(v2rayConfig) ?: "" result.domainPort = if (retMore.first) retMore.second else retOut.second result.guid = guid return result @@ -726,8 +726,7 @@ object V2rayConfigManager { V2rayConfig.OutboundBean.StreamSettingsBean.SockoptBean( dialerProxy = outbound.tag ) - if (nextNode.configType == EConfigType.WIREGUARD) - { + if (nextNode.configType == EConfigType.WIREGUARD) { domainPort = nextNode.getServerAddressAndPort() } } From 1f25d6a000d53e2dc8f2000c050378bed5c2bc6e Mon Sep 17 00:00:00 2001 From: Pk-web6936 <202365630+Pk-web6936@users.noreply.github.com> Date: Thu, 17 Apr 2025 11:59:56 +0330 Subject: [PATCH 13/81] Update dependencies (#4504) * Update libs.versions.toml * Update libs.versions.toml --- V2rayNG/gradle/libs.versions.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/V2rayNG/gradle/libs.versions.toml b/V2rayNG/gradle/libs.versions.toml index 76d7c562..6a107010 100644 --- a/V2rayNG/gradle/libs.versions.toml +++ b/V2rayNG/gradle/libs.versions.toml @@ -12,10 +12,10 @@ material = "1.12.0" activity = "1.10.1" constraintlayout = "2.2.1" mmkvStatic = "1.3.12" -gson = "2.13.0" +gson = "2.12.1" quickieFoss = "1.14.0" kotlinxCoroutinesAndroid = "1.10.2" -kotlinxCoroutinesCore = "1.10.1" +kotlinxCoroutinesCore = "1.10.2" swiperefreshlayout = "1.1.0" toasty = "1.5.2" editorkit = "2.9.0" From b862a0dc655b34d9ee8e22e659c14a7745f88e90 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Thu, 17 Apr 2025 16:40:53 +0800 Subject: [PATCH 14/81] Update AndroidLibXrayLite --- AndroidLibXrayLite | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AndroidLibXrayLite b/AndroidLibXrayLite index 2715a3b1..08aa01a2 160000 --- a/AndroidLibXrayLite +++ b/AndroidLibXrayLite @@ -1 +1 @@ -Subproject commit 2715a3b110f64e039a4b8d2c8ca0cb5a9a6b0958 +Subproject commit 08aa01a2c27447ae08bacd932ea2df8a08bb5580 From 56e33e6cdd1633f8f02e59f50f3b4a46af68e461 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Fri, 18 Apr 2025 10:22:17 +0800 Subject: [PATCH 15/81] Update gradle.properties --- V2rayNG/gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/V2rayNG/gradle.properties b/V2rayNG/gradle.properties index 6f65f16e..a1250797 100644 --- a/V2rayNG/gradle.properties +++ b/V2rayNG/gradle.properties @@ -6,7 +6,7 @@ # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8 +org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. For more details, visit # https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects From cfa9c19c9415fdc421cbe0f51b8914faa523a72d Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Fri, 18 Apr 2025 10:22:44 +0800 Subject: [PATCH 16/81] Clean code --- .../main/java/com/v2ray/ang/dto/ProfileLiteItem.kt | 9 --------- .../src/main/java/com/v2ray/ang/dto/V2rayConfig.kt | 13 +++---------- 2 files changed, 3 insertions(+), 19 deletions(-) delete mode 100644 V2rayNG/app/src/main/java/com/v2ray/ang/dto/ProfileLiteItem.kt diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/ProfileLiteItem.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/ProfileLiteItem.kt deleted file mode 100644 index 12995abd..00000000 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/ProfileLiteItem.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.v2ray.ang.dto - -data class ProfileLiteItem( - val configType: EConfigType, - var subscriptionId: String = "", - var remarks: String = "", - var server: String?, - var serverPort: Int?, -) \ No newline at end of file diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt index 00de185d..cc8c713e 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt @@ -1,25 +1,18 @@ package com.v2ray.ang.dto -import android.text.TextUtils -import com.google.gson.GsonBuilder -import com.google.gson.JsonPrimitive -import com.google.gson.JsonSerializationContext -import com.google.gson.JsonSerializer import com.google.gson.annotations.SerializedName -import com.google.gson.reflect.TypeToken import com.v2ray.ang.AppConfig import com.v2ray.ang.dto.V2rayConfig.OutboundBean.OutSettingsBean.ServersBean import com.v2ray.ang.dto.V2rayConfig.OutboundBean.OutSettingsBean.VnextBean import com.v2ray.ang.dto.V2rayConfig.OutboundBean.OutSettingsBean.VnextBean.UsersBean import com.v2ray.ang.dto.V2rayConfig.OutboundBean.OutSettingsBean.WireGuardBean import com.v2ray.ang.util.Utils -import java.lang.reflect.Type data class V2rayConfig( var remarks: String? = null, var stats: Any? = null, val log: LogBean, - var policy: PolicyBean?, + var policy: PolicyBean? = null, val inbounds: ArrayList, var outbounds: ArrayList, var dns: DnsBean? = null, @@ -36,7 +29,7 @@ data class V2rayConfig( data class LogBean( val access: String, val error: String, - var loglevel: String?, + var loglevel: String? = null, val dnsLog: Boolean? = null ) @@ -46,7 +39,7 @@ data class V2rayConfig( var protocol: String, var listen: String? = null, val settings: Any? = null, - val sniffing: SniffingBean?, + val sniffing: SniffingBean? = null, val streamSettings: Any? = null, val allocate: Any? = null ) { From 12a9ee262cd0b76390779e495541e12d087d7658 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Fri, 18 Apr 2025 10:24:54 +0800 Subject: [PATCH 17/81] Revert "Update gradle.properties" This reverts commit 56e33e6cdd1633f8f02e59f50f3b4a46af68e461. --- V2rayNG/gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/V2rayNG/gradle.properties b/V2rayNG/gradle.properties index a1250797..6f65f16e 100644 --- a/V2rayNG/gradle.properties +++ b/V2rayNG/gradle.properties @@ -6,7 +6,7 @@ # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 +org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8 # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. For more details, visit # https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects From 4ac0547e2208d5b152f95c4f09bf2c7158924aa8 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Fri, 18 Apr 2025 14:12:14 +0800 Subject: [PATCH 18/81] Resolves hostnames in config (#4508) * Removes IP resolution and resolves in config * Resolves hostnames to multiple IPs for DNS * Improves custom config handling --- .../java/com/v2ray/ang/dto/V2rayConfig.kt | 5 +++ .../main/java/com/v2ray/ang/fmt/FmtBase.kt | 4 --- .../main/java/com/v2ray/ang/fmt/HttpFmt.kt | 2 +- .../java/com/v2ray/ang/fmt/ShadowsocksFmt.kt | 2 +- .../main/java/com/v2ray/ang/fmt/SocksFmt.kt | 2 +- .../main/java/com/v2ray/ang/fmt/TrojanFmt.kt | 2 +- .../main/java/com/v2ray/ang/fmt/VlessFmt.kt | 2 +- .../main/java/com/v2ray/ang/fmt/VmessFmt.kt | 2 +- .../java/com/v2ray/ang/fmt/WireguardFmt.kt | 2 +- .../v2ray/ang/handler/V2rayConfigManager.kt | 31 +++++++++++++++++++ .../main/java/com/v2ray/ang/util/HttpUtil.kt | 26 +++++++++------- 11 files changed, 57 insertions(+), 23 deletions(-) diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt index cc8c713e..aa749c31 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt @@ -583,4 +583,9 @@ data class V2rayConfig( return null } + fun getAllProxyOutbound(): List { + return outbounds.filter { outbound -> + EConfigType.entries.any { it.name.equals(outbound.protocol, ignoreCase = true) } + } + } } \ No newline at end of file diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt index bc3229fe..9cea5544 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt @@ -302,8 +302,4 @@ open class FmtBase { } } - fun resolveHostToIP(server: String?): String { - return HttpUtil.resolveHostToIP(server.orEmpty(), MmkvManager.decodeSettingsBool(AppConfig.PREF_PREFER_IPV6) == true) - } - } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/HttpFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/HttpFmt.kt index 2ff28d8c..faac0230 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/HttpFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/HttpFmt.kt @@ -16,7 +16,7 @@ object HttpFmt : FmtBase() { val outboundBean = OutboundBean.create(EConfigType.HTTP) outboundBean?.settings?.servers?.first()?.let { server -> - server.address = resolveHostToIP(profileItem.server) + server.address = profileItem.server.orEmpty() server.port = profileItem.serverPort.orEmpty().toInt() if (profileItem.username.isNotNullEmpty()) { val socksUsersBean = OutboundBean.OutSettingsBean.ServersBean.SocksUsersBean() diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/ShadowsocksFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/ShadowsocksFmt.kt index d0acc28d..e3c0edc5 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/ShadowsocksFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/ShadowsocksFmt.kt @@ -134,7 +134,7 @@ object ShadowsocksFmt : FmtBase() { val outboundBean = OutboundBean.create(EConfigType.SHADOWSOCKS) outboundBean?.settings?.servers?.first()?.let { server -> - server.address = resolveHostToIP(profileItem.server) + server.address = profileItem.server.orEmpty() server.port = profileItem.serverPort.orEmpty().toInt() server.password = profileItem.password server.method = profileItem.method diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt index fad0bb4c..f37030c2 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt @@ -63,7 +63,7 @@ object SocksFmt : FmtBase() { val outboundBean = OutboundBean.create(EConfigType.SOCKS) outboundBean?.settings?.servers?.first()?.let { server -> - server.address = resolveHostToIP(profileItem.server) + server.address = profileItem.server.orEmpty() server.port = profileItem.serverPort.orEmpty().toInt() if (profileItem.username.isNotNullEmpty()) { val socksUsersBean = OutboundBean.OutSettingsBean.ServersBean.SocksUsersBean() diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt index 48dd8f4e..f9c06299 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt @@ -63,7 +63,7 @@ object TrojanFmt : FmtBase() { val outboundBean = OutboundBean.create(EConfigType.TROJAN) outboundBean?.settings?.servers?.first()?.let { server -> - server.address = resolveHostToIP(profileItem.server) + server.address = profileItem.server.orEmpty() server.port = profileItem.serverPort.orEmpty().toInt() server.password = profileItem.password server.flow = profileItem.flow diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VlessFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VlessFmt.kt index 1438cd7e..ffa8f28e 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VlessFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VlessFmt.kt @@ -59,7 +59,7 @@ object VlessFmt : FmtBase() { val outboundBean = OutboundBean.create(EConfigType.VLESS) outboundBean?.settings?.vnext?.first()?.let { vnext -> - vnext.address = resolveHostToIP(profileItem.server) + vnext.address = profileItem.server.orEmpty() vnext.port = profileItem.serverPort.orEmpty().toInt() vnext.users[0].id = profileItem.password.orEmpty() vnext.users[0].encryption = profileItem.method diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt index cb157e1e..3e56b84e 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt @@ -171,7 +171,7 @@ object VmessFmt : FmtBase() { val outboundBean = OutboundBean.create(EConfigType.VMESS) outboundBean?.settings?.vnext?.first()?.let { vnext -> - vnext.address = resolveHostToIP(profileItem.server) + vnext.address = profileItem.server.orEmpty() vnext.port = profileItem.serverPort.orEmpty().toInt() vnext.users[0].id = profileItem.password.orEmpty() vnext.users[0].security = profileItem.method diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt index 44ce1a04..e2bc5f90 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt @@ -113,7 +113,7 @@ object WireguardFmt : FmtBase() { wireguard.peers?.firstOrNull()?.let { peer -> peer.publicKey = profileItem.publicKey.orEmpty() peer.preSharedKey = profileItem.preSharedKey?.takeIf { it.isNotEmpty() } - peer.endpoint = Utils.getIpv6Address(resolveHostToIP(profileItem.server)) + ":${profileItem.serverPort}" + peer.endpoint = Utils.getIpv6Address(profileItem.server) + ":${profileItem.serverPort}" } wireguard.mtu = profileItem.mtu wireguard.reserved = profileItem.reserved?.takeIf { it.isNotBlank() }?.split(",")?.filter { it.isNotBlank() }?.map { it.trim().toInt() } 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 51c66523..18b130df 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 @@ -46,6 +46,7 @@ import com.v2ray.ang.fmt.TrojanFmt import com.v2ray.ang.fmt.VlessFmt import com.v2ray.ang.fmt.VmessFmt import com.v2ray.ang.fmt.WireguardFmt +import com.v2ray.ang.util.HttpUtil import com.v2ray.ang.util.JsonUtil import com.v2ray.ang.util.Utils @@ -150,6 +151,8 @@ object V2rayConfigManager { v2rayConfig.policy = null } + resolveProxyDomainsToHosts(v2rayConfig) + result.status = true result.content = JsonUtil.toJsonPretty(v2rayConfig) ?: "" result.domainPort = if (retMore.first) retMore.second else retOut.second @@ -763,4 +766,32 @@ object V2rayConfigManager { } + private fun resolveProxyDomainsToHosts(v2rayConfig: V2rayConfig) { + val proxyOutboundList = v2rayConfig.getAllProxyOutbound() + val dns = v2rayConfig.dns ?: return + + val newHosts = dns.hosts?.toMutableMap() ?: mutableMapOf() + + for (item in proxyOutboundList) { + val domain = item.getServerAddress() + if (domain.isNullOrEmpty()) continue + + if (newHosts.containsKey(domain)) continue + + val resolvedIps = HttpUtil.resolveHostToIP( + domain, + MmkvManager.decodeSettingsBool(AppConfig.PREF_PREFER_IPV6) == true + ) + + if (resolvedIps.isEmpty()) continue + + newHosts[domain] = if (resolvedIps.size == 1) { + resolvedIps[0] + } else { + resolvedIps + } + } + + dns.hosts = newHosts + } } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt index b7caa4e4..d9c3d86b 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt @@ -10,6 +10,7 @@ import java.io.IOException import java.net.HttpURLConnection import java.net.IDN import java.net.InetAddress +import java.net.Inet6Address import java.net.InetSocketAddress import java.net.Proxy import java.net.URL @@ -40,37 +41,38 @@ object HttpUtil { * @param ipv6Preferred Whether to prefer IPv6 addresses, defaults to false * @return The resolved IP address or the original input (if it's already an IP or resolution fails) */ - fun resolveHostToIP(host: String, ipv6Preferred: Boolean = false): String { + fun resolveHostToIP(host: String, ipv6Preferred: Boolean = false): List { try { - // If it's already an IP address, return it directly + // If it's already an IP address, return it as a list if (Utils.isPureIpAddress(host)) { - return host + return listOf(host) } // Get all IP addresses val addresses = InetAddress.getAllByName(host) if (addresses.isEmpty()) { - return host + return emptyList() } // Sort addresses based on preference val sortedAddresses = if (ipv6Preferred) { - // IPv6 preferred (size 16 first, then size 4) - addresses.sortedByDescending { it.address.size } + addresses.sortedWith(compareByDescending { it is Inet6Address }) } else { - // IPv4 preferred (size 4 first, then size 16) - addresses.sortedBy { it.address.size } + addresses.sortedWith(compareBy { it is Inet6Address }) } - Log.i(AppConfig.TAG, "Resolved IPs for $host: ${sortedAddresses.joinToString { it.hostAddress ?: "unknown" }}") - // Return the first address after sorting - return sortedAddresses.first().hostAddress ?: host + val ipList = sortedAddresses.mapNotNull { it.hostAddress } + + Log.i(AppConfig.TAG, "Resolved IPs for $host: ${ipList.joinToString()}") + + return ipList } catch (e: Exception) { Log.e(AppConfig.TAG, "Failed to resolve host to IP", e) - return host + return emptyList() } } + /** * Retrieves the content of a URL as a string. * From 864c63987e9a6793cd0af0102c992cee60f241d0 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Fri, 18 Apr 2025 16:49:51 +0800 Subject: [PATCH 19/81] Adds domain strategy option to sockopt (#4511) * Adds domain strategy option to sockopt * Simplifies sockopt handling in V2Ray config --- .../java/com/v2ray/ang/dto/V2rayConfig.kt | 15 +++++++++++- .../v2ray/ang/handler/V2rayConfigManager.kt | 23 +++++++------------ 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt index aa749c31..f4dedf22 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt @@ -292,7 +292,8 @@ data class V2rayConfig( var tcpFastOpen: Boolean? = null, var tproxy: String? = null, var mark: Int? = null, - var dialerProxy: String? = null + var dialerProxy: String? = null, + var domainStrategy: String? = null ) data class TlsSettingsBean( @@ -507,6 +508,18 @@ data class V2rayConfig( } return null } + + fun ensureSockopt(): V2rayConfig.OutboundBean.StreamSettingsBean.SockoptBean { + val stream = streamSettings ?: V2rayConfig.OutboundBean.StreamSettingsBean().also { + streamSettings = it + } + + val sockopt = stream.sockopt ?: V2rayConfig.OutboundBean.StreamSettingsBean.SockoptBean().also { + stream.sockopt = it + } + + return sockopt + } } data class DnsBean( 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 18b130df..42ac0a3e 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 @@ -702,13 +702,7 @@ object V2rayConfigManager { updateOutboundWithGlobalSettings(prevOutbound) prevOutbound.tag = TAG_PROXY + "2" v2rayConfig.outbounds.add(prevOutbound) - if (outbound.streamSettings == null) { - outbound.streamSettings = V2rayConfig.OutboundBean.StreamSettingsBean() - } - outbound.streamSettings?.sockopt = - V2rayConfig.OutboundBean.StreamSettingsBean.SockoptBean( - dialerProxy = prevOutbound.tag - ) + outbound.ensureSockopt().dialerProxy = prevOutbound.tag domainPort = prevNode.getServerAddressAndPort() } } @@ -722,13 +716,7 @@ object V2rayConfigManager { nextOutbound.tag = TAG_PROXY v2rayConfig.outbounds.add(0, nextOutbound) outbound.tag = TAG_PROXY + "1" - if (nextOutbound.streamSettings == null) { - nextOutbound.streamSettings = V2rayConfig.OutboundBean.StreamSettingsBean() - } - nextOutbound.streamSettings?.sockopt = - V2rayConfig.OutboundBean.StreamSettingsBean.SockoptBean( - dialerProxy = outbound.tag - ) + nextOutbound.ensureSockopt().dialerProxy = outbound.tag if (nextNode.configType == EConfigType.WIREGUARD) { domainPort = nextNode.getServerAddressAndPort() } @@ -772,6 +760,8 @@ object V2rayConfigManager { val newHosts = dns.hosts?.toMutableMap() ?: mutableMapOf() + val preferIpv6 = MmkvManager.decodeSettingsBool(AppConfig.PREF_PREFER_IPV6) == true + for (item in proxyOutboundList) { val domain = item.getServerAddress() if (domain.isNullOrEmpty()) continue @@ -780,11 +770,14 @@ object V2rayConfigManager { val resolvedIps = HttpUtil.resolveHostToIP( domain, - MmkvManager.decodeSettingsBool(AppConfig.PREF_PREFER_IPV6) == true + preferIpv6 ) if (resolvedIps.isEmpty()) continue + item.ensureSockopt().domainStrategy = + if (preferIpv6) "UseIPv6v4" else "UseIPv4v6" + newHosts[domain] = if (resolvedIps.size == 1) { resolvedIps[0] } else { From 21175f41ec8bc5a5ea2d8968aa2f76548cc290f1 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Fri, 18 Apr 2025 17:19:40 +0800 Subject: [PATCH 20/81] Update AndroidLibXrayLite --- AndroidLibXrayLite | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AndroidLibXrayLite b/AndroidLibXrayLite index 08aa01a2..c53ff63a 160000 --- a/AndroidLibXrayLite +++ b/AndroidLibXrayLite @@ -1 +1 @@ -Subproject commit 08aa01a2c27447ae08bacd932ea2df8a08bb5580 +Subproject commit c53ff63a3be2583d4b90f6819dfb0266cada0e18 From 46bc1a49dfbdda81c6f008334c1cdb6a09a02c73 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Fri, 18 Apr 2025 17:20:21 +0800 Subject: [PATCH 21/81] Refactor reference code with libv2ray remove protect --- .../v2ray/ang/service/V2RayServiceManager.kt | 53 +++++++++---------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt index de02ac6e..8e2c85c6 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt @@ -95,7 +95,9 @@ object V2RayServiceManager { * @param context The context from which the service is started. */ private fun startContextService(context: Context) { - if (coreController.isRunning) return + if (coreController.isRunning) { + return + } val guid = MmkvManager.getSelectServer() ?: return val config = MmkvManager.decodeServerConfig(guid) ?: return if (config.configType != EConfigType.CUSTOM @@ -128,12 +130,13 @@ object V2RayServiceManager { * Starts the V2Ray core service. */ fun startCoreLoop(): Boolean { - val service = getService() ?: return false - val guid = MmkvManager.getSelectServer() ?: return false - val config = MmkvManager.decodeServerConfig(guid) ?: return false if (coreController.isRunning) { return false } + + val service = getService() ?: return false + val guid = MmkvManager.getSelectServer() ?: return false + val config = MmkvManager.decodeServerConfig(guid) ?: return false val result = V2rayConfigManager.getV2rayConfig(service, guid) if (!result.status) return false @@ -224,30 +227,34 @@ object V2RayServiceManager { * Also fetches remote IP information if the delay test was successful. */ private fun measureV2rayDelay() { + if (coreController.isRunning == false) { + return + } + CoroutineScope(Dispatchers.IO).launch { val service = getService() ?: return@launch var time = -1L - var errstr = "" - if (coreController.isRunning) { + var errorStr = "" + + try { + time = coreController.measureDelay(SettingsManager.getDelayTestUrl()) + } catch (e: Exception) { + Log.e(AppConfig.TAG, "Failed to measure delay with primary URL", e) + errorStr = e.message?.substringAfter("\":") ?: "empty message" + } + if (time == -1L) { try { - time = coreController.measureDelay(SettingsManager.getDelayTestUrl()) + time = coreController.measureDelay(SettingsManager.getDelayTestUrl(true)) } catch (e: Exception) { - Log.e(AppConfig.TAG, "Failed to measure delay with primary URL", e) - errstr = e.message?.substringAfter("\":") ?: "empty message" - } - if (time == -1L) { - try { - time = coreController.measureDelay(SettingsManager.getDelayTestUrl(true)) - } catch (e: Exception) { - Log.e(AppConfig.TAG, "Failed to measure delay with alternative URL", e) - errstr = e.message?.substringAfter("\":") ?: "empty message" - } + Log.e(AppConfig.TAG, "Failed to measure delay with alternative URL", e) + errorStr = e.message?.substringAfter("\":") ?: "empty message" } } + val result = if (time >= 0) { service.getString(R.string.connection_test_available, time) } else { - service.getString(R.string.connection_test_error, errstr) + service.getString(R.string.connection_test_error, errorStr) } MessageUtil.sendMsg2UI(service, AppConfig.MSG_MEASURE_DELAY_SUCCESS, result) @@ -296,16 +303,6 @@ object V2RayServiceManager { } } - /** - * Protects a socket from being routed through the VPN. - * @param l The socket file descriptor. - * @return True if protection was successful, false otherwise. - */ - override fun protect(l: Long): Boolean { - val serviceControl = serviceControl?.get() ?: return true - return serviceControl.vpnProtect(l.toInt()) - } - /** * Called when V2Ray core emits status information. * @param l Status code. From 5bf7c98cd313fff0ab863bbe9c6b4fb57256f610 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Fri, 18 Apr 2025 20:02:55 +0800 Subject: [PATCH 22/81] Refactor outbound related code --- .../java/com/v2ray/ang/dto/V2rayConfig.kt | 48 ---- .../main/java/com/v2ray/ang/fmt/FmtBase.kt | 151 ------------ .../main/java/com/v2ray/ang/fmt/HttpFmt.kt | 3 +- .../java/com/v2ray/ang/fmt/Hysteria2Fmt.kt | 3 +- .../java/com/v2ray/ang/fmt/ShadowsocksFmt.kt | 31 +-- .../main/java/com/v2ray/ang/fmt/SocksFmt.kt | 3 +- .../main/java/com/v2ray/ang/fmt/TrojanFmt.kt | 31 +-- .../main/java/com/v2ray/ang/fmt/VlessFmt.kt | 31 +-- .../main/java/com/v2ray/ang/fmt/VmessFmt.kt | 31 +-- .../java/com/v2ray/ang/fmt/WireguardFmt.kt | 3 +- .../v2ray/ang/handler/V2rayConfigManager.kt | 224 ++++++++++++++++-- .../main/java/com/v2ray/ang/util/HttpUtil.kt | 2 +- 12 files changed, 228 insertions(+), 333 deletions(-) diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt index f4dedf22..0d08df47 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt @@ -2,10 +2,6 @@ package com.v2ray.ang.dto import com.google.gson.annotations.SerializedName import com.v2ray.ang.AppConfig -import com.v2ray.ang.dto.V2rayConfig.OutboundBean.OutSettingsBean.ServersBean -import com.v2ray.ang.dto.V2rayConfig.OutboundBean.OutSettingsBean.VnextBean -import com.v2ray.ang.dto.V2rayConfig.OutboundBean.OutSettingsBean.VnextBean.UsersBean -import com.v2ray.ang.dto.V2rayConfig.OutboundBean.OutSettingsBean.WireGuardBean import com.v2ray.ang.util.Utils data class V2rayConfig( @@ -70,50 +66,6 @@ data class V2rayConfig( val sendThrough: String? = null, var mux: MuxBean? = MuxBean(false) ) { - companion object { - fun create(configType: EConfigType): OutboundBean? { - return when (configType) { - EConfigType.VMESS, - EConfigType.VLESS -> - return OutboundBean( - protocol = configType.name.lowercase(), - settings = OutSettingsBean( - vnext = listOf( - VnextBean( - users = listOf(UsersBean()) - ) - ) - ), - streamSettings = StreamSettingsBean() - ) - - EConfigType.SHADOWSOCKS, - EConfigType.SOCKS, - EConfigType.HTTP, - EConfigType.TROJAN, - EConfigType.HYSTERIA2 -> - return OutboundBean( - protocol = configType.name.lowercase(), - settings = OutSettingsBean( - servers = listOf(ServersBean()) - ), - streamSettings = StreamSettingsBean() - ) - - EConfigType.WIREGUARD -> - return OutboundBean( - protocol = configType.name.lowercase(), - settings = OutSettingsBean( - secretKey = "", - peers = listOf(WireGuardBean()) - ) - ) - - EConfigType.CUSTOM -> null - } - } - } - data class OutSettingsBean( var vnext: List? = null, var fragment: FragmentBean? = null, diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt index 9cea5544..cc9a70ff 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt @@ -1,22 +1,9 @@ package com.v2ray.ang.fmt -import android.text.TextUtils import com.v2ray.ang.AppConfig import com.v2ray.ang.dto.NetworkType import com.v2ray.ang.dto.ProfileItem -import com.v2ray.ang.dto.V2rayConfig.OutboundBean.StreamSettingsBean -import com.v2ray.ang.dto.V2rayConfig.OutboundBean.StreamSettingsBean.GrpcSettingsBean -import com.v2ray.ang.dto.V2rayConfig.OutboundBean.StreamSettingsBean.HttpSettingsBean -import com.v2ray.ang.dto.V2rayConfig.OutboundBean.StreamSettingsBean.HttpupgradeSettingsBean -import com.v2ray.ang.dto.V2rayConfig.OutboundBean.StreamSettingsBean.KcpSettingsBean -import com.v2ray.ang.dto.V2rayConfig.OutboundBean.StreamSettingsBean.TcpSettingsBean -import com.v2ray.ang.dto.V2rayConfig.OutboundBean.StreamSettingsBean.TlsSettingsBean -import com.v2ray.ang.dto.V2rayConfig.OutboundBean.StreamSettingsBean.WsSettingsBean -import com.v2ray.ang.dto.V2rayConfig.OutboundBean.StreamSettingsBean.XhttpSettingsBean import com.v2ray.ang.extension.isNotNullEmpty -import com.v2ray.ang.handler.MmkvManager -import com.v2ray.ang.util.HttpUtil -import com.v2ray.ang.util.JsonUtil import com.v2ray.ang.util.Utils import java.net.URI @@ -162,144 +149,6 @@ open class FmtBase { return dicQuery } - fun populateTransportSettings( - streamSettings: StreamSettingsBean, - transport: String, - headerType: String?, - host: String?, - path: String?, - seed: String?, - quicSecurity: String?, - key: String?, - mode: String?, - serviceName: String?, - authority: String?, - xhttpMode: String?, - xhttpExtra: String?, - ): String? { - var sni: String? = null - streamSettings.network = if (transport.isEmpty()) NetworkType.TCP.type else transport - when (streamSettings.network) { - NetworkType.TCP.type -> { - val tcpSetting = TcpSettingsBean() - if (headerType == AppConfig.HEADER_TYPE_HTTP) { - tcpSetting.header.type = AppConfig.HEADER_TYPE_HTTP - if (!TextUtils.isEmpty(host) || !TextUtils.isEmpty(path)) { - val requestObj = TcpSettingsBean.HeaderBean.RequestBean() - requestObj.headers.Host = host.orEmpty().split(",").map { it.trim() }.filter { it.isNotEmpty() } - requestObj.path = path.orEmpty().split(",").map { it.trim() }.filter { it.isNotEmpty() } - tcpSetting.header.request = requestObj - sni = requestObj.headers.Host?.getOrNull(0) - } - } else { - tcpSetting.header.type = "none" - sni = host - } - streamSettings.tcpSettings = tcpSetting - } - NetworkType.KCP.type -> { - val kcpsetting = KcpSettingsBean() - kcpsetting.header.type = headerType ?: "none" - if (seed.isNullOrEmpty()) { - kcpsetting.seed = null - } else { - kcpsetting.seed = seed - } - if (host.isNullOrEmpty()) { - kcpsetting.header.domain = null - } else { - kcpsetting.header.domain = host - } - streamSettings.kcpSettings = kcpsetting - } - - NetworkType.WS.type -> { - val wssetting = WsSettingsBean() - wssetting.headers.Host = host.orEmpty() - sni = host - wssetting.path = path ?: "/" - streamSettings.wsSettings = wssetting - } - - NetworkType.HTTP_UPGRADE.type -> { - val httpupgradeSetting = HttpupgradeSettingsBean() - httpupgradeSetting.host = host.orEmpty() - sni = host - httpupgradeSetting.path = path ?: "/" - streamSettings.httpupgradeSettings = httpupgradeSetting - } - - NetworkType.XHTTP.type -> { - val xhttpSetting = XhttpSettingsBean() - xhttpSetting.host = host.orEmpty() - sni = host - xhttpSetting.path = path ?: "/" - xhttpSetting.mode = xhttpMode - xhttpSetting.extra = JsonUtil.parseString(xhttpExtra) - streamSettings.xhttpSettings = xhttpSetting - } - - NetworkType.H2.type, NetworkType.HTTP.type -> { - streamSettings.network = NetworkType.H2.type - val h2Setting = HttpSettingsBean() - h2Setting.host = host.orEmpty().split(",").map { it.trim() }.filter { it.isNotEmpty() } - sni = h2Setting.host.getOrNull(0) - h2Setting.path = path ?: "/" - streamSettings.httpSettings = h2Setting - } - -// "quic" -> { -// val quicsetting = QuicSettingBean() -// quicsetting.security = quicSecurity ?: "none" -// quicsetting.key = key.orEmpty() -// quicsetting.header.type = headerType ?: "none" -// quicSettings = quicsetting -// } - - NetworkType.GRPC.type -> { - val grpcSetting = GrpcSettingsBean() - grpcSetting.multiMode = mode == "multi" - grpcSetting.serviceName = serviceName.orEmpty() - grpcSetting.authority = authority.orEmpty() - grpcSetting.idle_timeout = 60 - grpcSetting.health_check_timeout = 20 - sni = authority - streamSettings.grpcSettings = grpcSetting - } - } - return sni - } - - fun populateTlsSettings( - streamSettings: StreamSettingsBean, - streamSecurity: String, - allowInsecure: Boolean, - sni: String?, - fingerprint: String?, - alpns: String?, - publicKey: String?, - shortId: String?, - spiderX: String? - ) { - streamSettings.security = if (streamSecurity.isEmpty()) null else streamSecurity - if (streamSettings.security == null) return - val tlsSetting = TlsSettingsBean( - allowInsecure = allowInsecure, - serverName = if (sni.isNullOrEmpty()) null else sni, - fingerprint = if (fingerprint.isNullOrEmpty()) null else fingerprint, - alpn = if (alpns.isNullOrEmpty()) null else alpns.split(",").map { it.trim() }.filter { it.isNotEmpty() }, - publicKey = if (publicKey.isNullOrEmpty()) null else publicKey, - shortId = if (shortId.isNullOrEmpty()) null else shortId, - spiderX = if (spiderX.isNullOrEmpty()) null else spiderX, - ) - if (streamSettings.security == AppConfig.TLS) { - streamSettings.tlsSettings = tlsSetting - streamSettings.realitySettings = null - } else if (streamSettings.security == AppConfig.REALITY) { - streamSettings.tlsSettings = null - streamSettings.realitySettings = tlsSetting - } - } } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/HttpFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/HttpFmt.kt index faac0230..4163737e 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/HttpFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/HttpFmt.kt @@ -4,6 +4,7 @@ import com.v2ray.ang.dto.EConfigType import com.v2ray.ang.dto.ProfileItem import com.v2ray.ang.dto.V2rayConfig.OutboundBean import com.v2ray.ang.extension.isNotNullEmpty +import com.v2ray.ang.handler.V2rayConfigManager object HttpFmt : FmtBase() { /** @@ -13,7 +14,7 @@ object HttpFmt : FmtBase() { * @return the converted OutboundBean object, or null if conversion fails */ fun toOutbound(profileItem: ProfileItem): OutboundBean? { - val outboundBean = OutboundBean.create(EConfigType.HTTP) + val outboundBean = V2rayConfigManager.createOutbound(EConfigType.HTTP) outboundBean?.settings?.servers?.first()?.let { server -> server.address = profileItem.server.orEmpty() diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/Hysteria2Fmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/Hysteria2Fmt.kt index 7a6abe08..80d127fb 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/Hysteria2Fmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/Hysteria2Fmt.kt @@ -9,6 +9,7 @@ import com.v2ray.ang.dto.V2rayConfig.OutboundBean import com.v2ray.ang.extension.idnHost import com.v2ray.ang.extension.isNotNullEmpty import com.v2ray.ang.handler.MmkvManager +import com.v2ray.ang.handler.V2rayConfigManager import com.v2ray.ang.util.Utils import java.net.URI @@ -144,7 +145,7 @@ object Hysteria2Fmt : FmtBase() { * @return the converted OutboundBean object, or null if conversion fails */ fun toOutbound(profileItem: ProfileItem): OutboundBean? { - val outboundBean = OutboundBean.create(EConfigType.HYSTERIA2) + val outboundBean = V2rayConfigManager.createOutbound(EConfigType.HYSTERIA2) return outboundBean } } \ No newline at end of file diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/ShadowsocksFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/ShadowsocksFmt.kt index e3c0edc5..e41e707e 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/ShadowsocksFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/ShadowsocksFmt.kt @@ -7,6 +7,7 @@ import com.v2ray.ang.dto.NetworkType import com.v2ray.ang.dto.ProfileItem import com.v2ray.ang.dto.V2rayConfig.OutboundBean import com.v2ray.ang.extension.idnHost +import com.v2ray.ang.handler.V2rayConfigManager import com.v2ray.ang.util.Utils import java.net.URI @@ -131,7 +132,7 @@ object ShadowsocksFmt : FmtBase() { * @return the converted OutboundBean object, or null if conversion fails */ fun toOutbound(profileItem: ProfileItem): OutboundBean? { - val outboundBean = OutboundBean.create(EConfigType.SHADOWSOCKS) + val outboundBean = V2rayConfigManager.createOutbound(EConfigType.SHADOWSOCKS) outboundBean?.settings?.servers?.first()?.let { server -> server.address = profileItem.server.orEmpty() @@ -141,35 +142,11 @@ object ShadowsocksFmt : FmtBase() { } val sni = outboundBean?.streamSettings?.let { - populateTransportSettings( - it, - profileItem.network.orEmpty(), - profileItem.headerType, - profileItem.host, - profileItem.path, - profileItem.seed, - profileItem.quicSecurity, - profileItem.quicKey, - profileItem.mode, - profileItem.serviceName, - profileItem.authority, - profileItem.xhttpMode, - profileItem.xhttpExtra - ) + V2rayConfigManager.populateTransportSettings(it, profileItem) } outboundBean?.streamSettings?.let { - populateTlsSettings( - it, - profileItem.security.orEmpty(), - profileItem.insecure == true, - if (profileItem.sni.isNullOrEmpty()) sni else profileItem.sni, - profileItem.fingerPrint, - profileItem.alpn, - profileItem.publicKey, - profileItem.shortId, - profileItem.spiderX, - ) + V2rayConfigManager.populateTlsSettings(it, profileItem, sni) } return outboundBean diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt index f37030c2..39013e4b 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt @@ -5,6 +5,7 @@ import com.v2ray.ang.dto.ProfileItem import com.v2ray.ang.dto.V2rayConfig.OutboundBean import com.v2ray.ang.extension.idnHost import com.v2ray.ang.extension.isNotNullEmpty +import com.v2ray.ang.handler.V2rayConfigManager import com.v2ray.ang.util.Utils import java.net.URI @@ -60,7 +61,7 @@ object SocksFmt : FmtBase() { * @return the converted OutboundBean object, or null if conversion fails */ fun toOutbound(profileItem: ProfileItem): OutboundBean? { - val outboundBean = OutboundBean.create(EConfigType.SOCKS) + val outboundBean = V2rayConfigManager.createOutbound(EConfigType.SOCKS) outboundBean?.settings?.servers?.first()?.let { server -> server.address = profileItem.server.orEmpty() diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt index f9c06299..efd83f9e 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt @@ -7,6 +7,7 @@ import com.v2ray.ang.dto.ProfileItem import com.v2ray.ang.dto.V2rayConfig.OutboundBean import com.v2ray.ang.extension.idnHost import com.v2ray.ang.handler.MmkvManager +import com.v2ray.ang.handler.V2rayConfigManager import com.v2ray.ang.util.Utils import java.net.URI @@ -60,7 +61,7 @@ object TrojanFmt : FmtBase() { * @return the converted OutboundBean object, or null if conversion fails */ fun toOutbound(profileItem: ProfileItem): OutboundBean? { - val outboundBean = OutboundBean.create(EConfigType.TROJAN) + val outboundBean = V2rayConfigManager.createOutbound(EConfigType.TROJAN) outboundBean?.settings?.servers?.first()?.let { server -> server.address = profileItem.server.orEmpty() @@ -70,35 +71,11 @@ object TrojanFmt : FmtBase() { } val sni = outboundBean?.streamSettings?.let { - populateTransportSettings( - it, - profileItem.network.orEmpty(), - profileItem.headerType, - profileItem.host, - profileItem.path, - profileItem.seed, - profileItem.quicSecurity, - profileItem.quicKey, - profileItem.mode, - profileItem.serviceName, - profileItem.authority, - profileItem.xhttpMode, - profileItem.xhttpExtra - ) + V2rayConfigManager.populateTransportSettings(it, profileItem) } outboundBean?.streamSettings?.let { - populateTlsSettings( - it, - profileItem.security.orEmpty(), - profileItem.insecure == true, - if (profileItem.sni.isNullOrEmpty()) sni else profileItem.sni, - profileItem.fingerPrint, - profileItem.alpn, - profileItem.publicKey, - profileItem.shortId, - profileItem.spiderX, - ) + V2rayConfigManager.populateTlsSettings(it, profileItem, sni) } return outboundBean diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VlessFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VlessFmt.kt index ffa8f28e..fe835ad3 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VlessFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VlessFmt.kt @@ -6,6 +6,7 @@ import com.v2ray.ang.dto.ProfileItem import com.v2ray.ang.dto.V2rayConfig.OutboundBean import com.v2ray.ang.extension.idnHost import com.v2ray.ang.handler.MmkvManager +import com.v2ray.ang.handler.V2rayConfigManager import com.v2ray.ang.util.Utils import java.net.URI @@ -56,7 +57,7 @@ object VlessFmt : FmtBase() { * @return the converted OutboundBean object, or null if conversion fails */ fun toOutbound(profileItem: ProfileItem): OutboundBean? { - val outboundBean = OutboundBean.create(EConfigType.VLESS) + val outboundBean = V2rayConfigManager.createOutbound(EConfigType.VLESS) outboundBean?.settings?.vnext?.first()?.let { vnext -> vnext.address = profileItem.server.orEmpty() @@ -67,35 +68,11 @@ object VlessFmt : FmtBase() { } val sni = outboundBean?.streamSettings?.let { - populateTransportSettings( - it, - profileItem.network.orEmpty(), - profileItem.headerType, - profileItem.host, - profileItem.path, - profileItem.seed, - profileItem.quicSecurity, - profileItem.quicKey, - profileItem.mode, - profileItem.serviceName, - profileItem.authority, - profileItem.xhttpMode, - profileItem.xhttpExtra - ) + V2rayConfigManager.populateTransportSettings(it, profileItem) } outboundBean?.streamSettings?.let { - populateTlsSettings( - it, - profileItem.security.orEmpty(), - profileItem.insecure == true, - if (profileItem.sni.isNullOrEmpty()) sni else profileItem.sni, - profileItem.fingerPrint, - profileItem.alpn, - profileItem.publicKey, - profileItem.shortId, - profileItem.spiderX, - ) + V2rayConfigManager.populateTlsSettings(it, profileItem, sni) } return outboundBean diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt index 3e56b84e..002a75db 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt @@ -11,6 +11,7 @@ import com.v2ray.ang.dto.VmessQRCode import com.v2ray.ang.extension.idnHost import com.v2ray.ang.extension.isNotNullEmpty import com.v2ray.ang.handler.MmkvManager +import com.v2ray.ang.handler.V2rayConfigManager import com.v2ray.ang.util.JsonUtil import com.v2ray.ang.util.Utils import java.net.URI @@ -168,7 +169,7 @@ object VmessFmt : FmtBase() { * @return the converted OutboundBean object, or null if conversion fails */ fun toOutbound(profileItem: ProfileItem): OutboundBean? { - val outboundBean = OutboundBean.create(EConfigType.VMESS) + val outboundBean = V2rayConfigManager.createOutbound(EConfigType.VMESS) outboundBean?.settings?.vnext?.first()?.let { vnext -> vnext.address = profileItem.server.orEmpty() @@ -178,35 +179,11 @@ object VmessFmt : FmtBase() { } val sni = outboundBean?.streamSettings?.let { - populateTransportSettings( - it, - profileItem.network.orEmpty(), - profileItem.headerType, - profileItem.host, - profileItem.path, - profileItem.seed, - profileItem.quicSecurity, - profileItem.quicKey, - profileItem.mode, - profileItem.serviceName, - profileItem.authority, - profileItem.xhttpMode, - profileItem.xhttpExtra - ) + V2rayConfigManager.populateTransportSettings(it, profileItem) } outboundBean?.streamSettings?.let { - populateTlsSettings( - it, - profileItem.security.orEmpty(), - profileItem.insecure == true, - if (profileItem.sni.isNullOrEmpty()) sni else profileItem.sni, - profileItem.fingerPrint, - profileItem.alpn, - null, - null, - null - ) + V2rayConfigManager.populateTlsSettings(it, profileItem, sni) } return outboundBean diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt index e2bc5f90..10d4992a 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt @@ -7,6 +7,7 @@ import com.v2ray.ang.dto.ProfileItem import com.v2ray.ang.dto.V2rayConfig.OutboundBean import com.v2ray.ang.extension.idnHost import com.v2ray.ang.extension.removeWhiteSpace +import com.v2ray.ang.handler.V2rayConfigManager import com.v2ray.ang.util.Utils import java.net.URI @@ -105,7 +106,7 @@ object WireguardFmt : FmtBase() { * @return the converted OutboundBean object, or null if conversion fails */ fun toOutbound(profileItem: ProfileItem): OutboundBean? { - val outboundBean = OutboundBean.create(EConfigType.WIREGUARD) + val outboundBean = V2rayConfigManager.createOutbound(EConfigType.WIREGUARD) outboundBean?.settings?.let { wireguard -> wireguard.secretKey = profileItem.secretKey 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 42ac0a3e..6110f600 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 @@ -36,6 +36,9 @@ import com.v2ray.ang.dto.NetworkType import com.v2ray.ang.dto.ProfileItem import com.v2ray.ang.dto.RulesetItem import com.v2ray.ang.dto.V2rayConfig +import com.v2ray.ang.dto.V2rayConfig.OutboundBean +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.HttpFmt @@ -733,27 +736,6 @@ object V2rayConfigManager { return returnPair } - /** - * Retrieves the proxy outbound configuration for the given profile item. - * - * @param profileItem The profile item for which to get the proxy outbound configuration. - * @return The proxy outbound configuration as a V2rayConfig.OutboundBean, or null if not found. - */ - fun getProxyOutbound(profileItem: ProfileItem): V2rayConfig.OutboundBean? { - return when (profileItem.configType) { - EConfigType.VMESS -> VmessFmt.toOutbound(profileItem) - EConfigType.CUSTOM -> null - EConfigType.SHADOWSOCKS -> ShadowsocksFmt.toOutbound(profileItem) - EConfigType.SOCKS -> SocksFmt.toOutbound(profileItem) - EConfigType.VLESS -> VlessFmt.toOutbound(profileItem) - EConfigType.TROJAN -> TrojanFmt.toOutbound(profileItem) - EConfigType.WIREGUARD -> WireguardFmt.toOutbound(profileItem) - EConfigType.HYSTERIA2 -> Hysteria2Fmt.toOutbound(profileItem) - EConfigType.HTTP -> HttpFmt.toOutbound(profileItem) - } - - } - private fun resolveProxyDomainsToHosts(v2rayConfig: V2rayConfig) { val proxyOutboundList = v2rayConfig.getAllProxyOutbound() val dns = v2rayConfig.dns ?: return @@ -787,4 +769,204 @@ object V2rayConfigManager { dns.hosts = newHosts } + + /** + * Retrieves the proxy outbound configuration for the given profile item. + * + * @param profileItem The profile item for which to get the proxy outbound configuration. + * @return The proxy outbound configuration as a V2rayConfig.OutboundBean, or null if not found. + */ + private fun getProxyOutbound(profileItem: ProfileItem): V2rayConfig.OutboundBean? { + return when (profileItem.configType) { + EConfigType.VMESS -> VmessFmt.toOutbound(profileItem) + EConfigType.CUSTOM -> null + EConfigType.SHADOWSOCKS -> ShadowsocksFmt.toOutbound(profileItem) + EConfigType.SOCKS -> SocksFmt.toOutbound(profileItem) + EConfigType.VLESS -> VlessFmt.toOutbound(profileItem) + EConfigType.TROJAN -> TrojanFmt.toOutbound(profileItem) + EConfigType.WIREGUARD -> WireguardFmt.toOutbound(profileItem) + EConfigType.HYSTERIA2 -> Hysteria2Fmt.toOutbound(profileItem) + EConfigType.HTTP -> HttpFmt.toOutbound(profileItem) + } + } + + fun createOutbound(configType: EConfigType): OutboundBean? { + return when (configType) { + EConfigType.VMESS, + EConfigType.VLESS -> + return OutboundBean( + protocol = configType.name.lowercase(), + settings = OutSettingsBean( + vnext = listOf( + OutSettingsBean.VnextBean( + users = listOf(OutSettingsBean.VnextBean.UsersBean()) + ) + ) + ), + streamSettings = StreamSettingsBean() + ) + + EConfigType.SHADOWSOCKS, + EConfigType.SOCKS, + EConfigType.HTTP, + EConfigType.TROJAN, + EConfigType.HYSTERIA2 -> + return OutboundBean( + protocol = configType.name.lowercase(), + settings = OutSettingsBean( + servers = listOf(OutSettingsBean.ServersBean()) + ), + streamSettings = StreamSettingsBean() + ) + + EConfigType.WIREGUARD -> + return OutboundBean( + protocol = configType.name.lowercase(), + settings = OutSettingsBean( + secretKey = "", + peers = listOf(OutSettingsBean.WireGuardBean()) + ) + ) + + EConfigType.CUSTOM -> null + } + } + + fun populateTransportSettings(streamSettings: StreamSettingsBean, profileItem: ProfileItem): String? { + val transport = profileItem.network.orEmpty() + val headerType = profileItem.headerType + val host = profileItem.host + val path = profileItem.path + val seed = profileItem.seed +// val quicSecurity = profileItem.quicSecurity +// val key = profileItem.quicKey + val mode = profileItem.mode + val serviceName = profileItem.serviceName + val authority = profileItem.authority + val xhttpMode = profileItem.xhttpMode + val xhttpExtra = profileItem.xhttpExtra + + var sni: String? = null + streamSettings.network = if (transport.isEmpty()) NetworkType.TCP.type else transport + when (streamSettings.network) { + NetworkType.TCP.type -> { + val tcpSetting = StreamSettingsBean.TcpSettingsBean() + if (headerType == AppConfig.HEADER_TYPE_HTTP) { + tcpSetting.header.type = AppConfig.HEADER_TYPE_HTTP + if (!TextUtils.isEmpty(host) || !TextUtils.isEmpty(path)) { + val requestObj = StreamSettingsBean.TcpSettingsBean.HeaderBean.RequestBean() + requestObj.headers.Host = host.orEmpty().split(",").map { it.trim() }.filter { it.isNotEmpty() } + requestObj.path = path.orEmpty().split(",").map { it.trim() }.filter { it.isNotEmpty() } + tcpSetting.header.request = requestObj + sni = requestObj.headers.Host?.getOrNull(0) + } + } else { + tcpSetting.header.type = "none" + sni = host + } + streamSettings.tcpSettings = tcpSetting + } + + NetworkType.KCP.type -> { + val kcpsetting = StreamSettingsBean.KcpSettingsBean() + kcpsetting.header.type = headerType ?: "none" + if (seed.isNullOrEmpty()) { + kcpsetting.seed = null + } else { + kcpsetting.seed = seed + } + if (host.isNullOrEmpty()) { + kcpsetting.header.domain = null + } else { + kcpsetting.header.domain = host + } + streamSettings.kcpSettings = kcpsetting + } + + NetworkType.WS.type -> { + val wssetting = StreamSettingsBean.WsSettingsBean() + wssetting.headers.Host = host.orEmpty() + sni = host + wssetting.path = path ?: "/" + streamSettings.wsSettings = wssetting + } + + NetworkType.HTTP_UPGRADE.type -> { + val httpupgradeSetting = StreamSettingsBean.HttpupgradeSettingsBean() + httpupgradeSetting.host = host.orEmpty() + sni = host + httpupgradeSetting.path = path ?: "/" + streamSettings.httpupgradeSettings = httpupgradeSetting + } + + NetworkType.XHTTP.type -> { + val xhttpSetting = StreamSettingsBean.XhttpSettingsBean() + xhttpSetting.host = host.orEmpty() + sni = host + xhttpSetting.path = path ?: "/" + xhttpSetting.mode = xhttpMode + xhttpSetting.extra = JsonUtil.parseString(xhttpExtra) + streamSettings.xhttpSettings = xhttpSetting + } + + NetworkType.H2.type, NetworkType.HTTP.type -> { + streamSettings.network = NetworkType.H2.type + val h2Setting = StreamSettingsBean.HttpSettingsBean() + h2Setting.host = host.orEmpty().split(",").map { it.trim() }.filter { it.isNotEmpty() } + sni = h2Setting.host.getOrNull(0) + h2Setting.path = path ?: "/" + streamSettings.httpSettings = h2Setting + } + +// "quic" -> { +// val quicsetting = QuicSettingBean() +// quicsetting.security = quicSecurity ?: "none" +// quicsetting.key = key.orEmpty() +// quicsetting.header.type = headerType ?: "none" +// quicSettings = quicsetting +// } + + NetworkType.GRPC.type -> { + val grpcSetting = StreamSettingsBean.GrpcSettingsBean() + grpcSetting.multiMode = mode == "multi" + grpcSetting.serviceName = serviceName.orEmpty() + grpcSetting.authority = authority.orEmpty() + grpcSetting.idle_timeout = 60 + grpcSetting.health_check_timeout = 20 + sni = authority + streamSettings.grpcSettings = grpcSetting + } + } + return sni + } + + fun populateTlsSettings(streamSettings: StreamSettingsBean, profileItem: ProfileItem, sniExt: String?) { + val streamSecurity = profileItem.security.orEmpty() + val allowInsecure = profileItem.insecure == true + val sni = if (profileItem.sni.isNullOrEmpty()) sniExt else profileItem.sni + val fingerprint = profileItem.fingerPrint + val alpns = profileItem.alpn + val publicKey = profileItem.publicKey + val shortId = profileItem.shortId + val spiderX = profileItem.spiderX + + streamSettings.security = if (streamSecurity.isEmpty()) null else streamSecurity + if (streamSettings.security == null) return + val tlsSetting = StreamSettingsBean.TlsSettingsBean( + allowInsecure = allowInsecure, + serverName = if (sni.isNullOrEmpty()) null else sni, + fingerprint = if (fingerprint.isNullOrEmpty()) null else fingerprint, + alpn = if (alpns.isNullOrEmpty()) null else alpns.split(",").map { it.trim() }.filter { it.isNotEmpty() }, + publicKey = if (publicKey.isNullOrEmpty()) null else publicKey, + shortId = if (shortId.isNullOrEmpty()) null else shortId, + spiderX = if (spiderX.isNullOrEmpty()) null else spiderX, + ) + if (streamSettings.security == AppConfig.TLS) { + streamSettings.tlsSettings = tlsSetting + streamSettings.realitySettings = null + } else if (streamSettings.security == AppConfig.REALITY) { + streamSettings.tlsSettings = null + streamSettings.realitySettings = tlsSetting + } + } } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt index d9c3d86b..371fa509 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt @@ -9,8 +9,8 @@ import com.v2ray.ang.util.Utils.urlDecode import java.io.IOException import java.net.HttpURLConnection import java.net.IDN -import java.net.InetAddress import java.net.Inet6Address +import java.net.InetAddress import java.net.InetSocketAddress import java.net.Proxy import java.net.URL From 4fcb3f9d069ff9ca0edcc9e296df393499ce38de Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Sat, 19 Apr 2025 10:24:15 +0800 Subject: [PATCH 23/81] Refactor ConfigResult --- .../java/com/v2ray/ang/dto/ConfigResult.kt | 2 +- .../v2ray/ang/handler/V2rayConfigManager.kt | 105 ++++++++---------- .../v2ray/ang/service/V2RayServiceManager.kt | 2 +- .../java/com/v2ray/ang/util/PluginUtil.kt | 18 ++- 4 files changed, 59 insertions(+), 68 deletions(-) diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/ConfigResult.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/ConfigResult.kt index c0b70c6a..c8870248 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/ConfigResult.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/ConfigResult.kt @@ -4,6 +4,6 @@ data class ConfigResult( var status: Boolean, var guid: String? = null, var content: String = "", - var domainPort: String? = null, + var socksPort: Int? = null, ) 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 6110f600..e1ab5c4e 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 @@ -107,8 +107,7 @@ object V2rayConfigManager { */ private fun getV2rayCustomConfig(guid: String, config: ProfileItem): ConfigResult { val raw = MmkvManager.decodeServerRaw(guid) ?: return ConfigResult(false) - val domainPort = config.getServerAddressAndPort() - return ConfigResult(true, guid, raw, domainPort) + return ConfigResult(true, guid, raw) } /** @@ -136,9 +135,12 @@ object V2rayConfigManager { inbounds(v2rayConfig) - val isPlugin = config.configType == EConfigType.HYSTERIA2 - val retOut = outbounds(v2rayConfig, config, isPlugin) ?: return result - val retMore = moreOutbounds(v2rayConfig, config.subscriptionId, isPlugin) + if (config.configType == EConfigType.HYSTERIA2) { + result.socksPort = plusOutbound(v2rayConfig, config) ?: return result + } else { + outbounds(v2rayConfig, config) ?: return result + moreOutbounds(v2rayConfig, config.subscriptionId) + } routing(v2rayConfig) @@ -158,7 +160,6 @@ object V2rayConfigManager { result.status = true result.content = JsonUtil.toJsonPretty(v2rayConfig) ?: "" - result.domainPort = if (retMore.first) retMore.second else retOut.second result.guid = guid return result } @@ -184,9 +185,12 @@ object V2rayConfigManager { val v2rayConfig = initV2rayConfig(context) ?: return result - val isPlugin = config.configType == EConfigType.HYSTERIA2 - val retOut = outbounds(v2rayConfig, config, isPlugin) ?: return result - val retMore = moreOutbounds(v2rayConfig, config.subscriptionId, isPlugin) + if (config.configType == EConfigType.HYSTERIA2) { + result.socksPort = plusOutbound(v2rayConfig, config) ?: return result + } else { + outbounds(v2rayConfig, config) ?: return result + moreOutbounds(v2rayConfig, config.subscriptionId) + } v2rayConfig.log.loglevel = MmkvManager.decodeSettingsString(AppConfig.PREF_LOGLEVEL) ?: "warning" v2rayConfig.inbounds.clear() @@ -202,7 +206,6 @@ object V2rayConfigManager { result.status = true result.content = JsonUtil.toJsonPretty(v2rayConfig) ?: "" - result.domainPort = if (retMore.first) retMore.second else retOut.second result.guid = guid return result } @@ -266,29 +269,7 @@ object V2rayConfigManager { return true } - private fun outbounds(v2rayConfig: V2rayConfig, config: ProfileItem, isPlugin: Boolean): Pair? { - if (isPlugin) { - val socksPort = Utils.findFreePort(listOf(100 + SettingsManager.getSocksPort(), 0)) - val outboundNew = V2rayConfig.OutboundBean( - mux = null, - protocol = EConfigType.SOCKS.name.lowercase(), - settings = V2rayConfig.OutboundBean.OutSettingsBean( - servers = listOf( - V2rayConfig.OutboundBean.OutSettingsBean.ServersBean( - address = LOOPBACK, - port = socksPort - ) - ) - ) - ) - if (v2rayConfig.outbounds.isNotEmpty()) { - v2rayConfig.outbounds[0] = outboundNew - } else { - v2rayConfig.outbounds.add(outboundNew) - } - return Pair(true, outboundNew.getServerAddressAndPort()) - } - + private fun outbounds(v2rayConfig: V2rayConfig, config: ProfileItem): Boolean? { val outbound = getProxyOutbound(config) ?: return null val ret = updateOutboundWithGlobalSettings(outbound) if (!ret) return null @@ -300,7 +281,7 @@ object V2rayConfigManager { } updateOutboundFragment(v2rayConfig) - return Pair(true, config.getServerAddressAndPort()) + return true } private fun fakedns(v2rayConfig: V2rayConfig) { @@ -672,27 +653,17 @@ object V2rayConfigManager { return true } - private fun moreOutbounds( - v2rayConfig: V2rayConfig, - subscriptionId: String, - isPlugin: Boolean - ): Pair { - val returnPair = Pair(false, "") - var domainPort: String = "" - - if (isPlugin) { - return returnPair - } + private fun moreOutbounds(v2rayConfig: V2rayConfig, subscriptionId: String): Boolean { //fragment proxy if (MmkvManager.decodeSettingsBool(AppConfig.PREF_FRAGMENT_ENABLED, false) == true) { - return returnPair + return false } if (subscriptionId.isEmpty()) { - return returnPair + return false } try { - val subItem = MmkvManager.decodeSubscription(subscriptionId) ?: return returnPair + val subItem = MmkvManager.decodeSubscription(subscriptionId) ?: return false //current proxy val outbound = v2rayConfig.outbounds[0] @@ -706,7 +677,6 @@ object V2rayConfigManager { prevOutbound.tag = TAG_PROXY + "2" v2rayConfig.outbounds.add(prevOutbound) outbound.ensureSockopt().dialerProxy = prevOutbound.tag - domainPort = prevNode.getServerAddressAndPort() } } @@ -720,20 +690,43 @@ object V2rayConfigManager { v2rayConfig.outbounds.add(0, nextOutbound) outbound.tag = TAG_PROXY + "1" nextOutbound.ensureSockopt().dialerProxy = outbound.tag - if (nextNode.configType == EConfigType.WIREGUARD) { - domainPort = nextNode.getServerAddressAndPort() - } } } } catch (e: Exception) { Log.e(AppConfig.TAG, "Failed to configure more outbounds", e) - return returnPair + return false } - if (domainPort.isNotEmpty()) { - return Pair(true, domainPort) + return true + } + + private fun plusOutbound(v2rayConfig: V2rayConfig, config: ProfileItem): Int? { + try { + val socksPort = Utils.findFreePort(listOf(100 + SettingsManager.getSocksPort(), 0)) + + val outboundNew = V2rayConfig.OutboundBean( + mux = null, + protocol = EConfigType.SOCKS.name.lowercase(), + settings = V2rayConfig.OutboundBean.OutSettingsBean( + servers = listOf( + V2rayConfig.OutboundBean.OutSettingsBean.ServersBean( + address = LOOPBACK, + port = socksPort + ) + ) + ) + ) + if (v2rayConfig.outbounds.isNotEmpty()) { + v2rayConfig.outbounds[0] = outboundNew + } else { + v2rayConfig.outbounds.add(outboundNew) + } + + return socksPort + } catch (e: Exception) { + Log.e(AppConfig.TAG, "Failed to configure plusOutbound", e) + return null } - return returnPair } private fun resolveProxyDomainsToHosts(v2rayConfig: V2rayConfig) { diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt index 8e2c85c6..95ab22cd 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt @@ -172,7 +172,7 @@ object V2RayServiceManager { NotificationService.startSpeedNotification(currentConfig) NotificationService.showNotification(currentConfig) - PluginUtil.runPlugin(service, config, result.domainPort) + PluginUtil.runPlugin(service, config, result.socksPort) } catch (e: Exception) { Log.e(AppConfig.TAG, "Failed to startup service", e) return false diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/util/PluginUtil.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/util/PluginUtil.kt index bcf30a3b..48e04b6d 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/util/PluginUtil.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/util/PluginUtil.kt @@ -23,20 +23,20 @@ object PluginUtil { * * @param context The context to use. * @param config The profile configuration. - * @param domainPort The domain and port information. + * @param socksPort The port information. */ - fun runPlugin(context: Context, config: ProfileItem?, domainPort: String?) { + fun runPlugin(context: Context, config: ProfileItem?, socksPort: Int?) { Log.i(AppConfig.TAG, "Starting plugin execution") - if (config == null) { + if (config == null || socksPort == null) { Log.w(AppConfig.TAG, "Cannot run plugin: config is null") return } - + try { if (config.configType == EConfigType.HYSTERIA2) { Log.i(AppConfig.TAG, "Running Hysteria2 plugin") - val configFile = genConfigHy2(context, config, domainPort) ?: return + val configFile = genConfigHy2(context, config, socksPort) ?: return val cmd = genCmdHy2(context, configFile) procService.runProcess(context, cmd) @@ -66,7 +66,7 @@ object PluginUtil { if (config?.configType?.equals(EConfigType.HYSTERIA2) == true) { val socksPort = Utils.findFreePort(listOf(0)) - val configFile = genConfigHy2(context, config, "0:${socksPort}") ?: return retFailure + val configFile = genConfigHy2(context, config, socksPort) ?: return retFailure val cmd = genCmdHy2(context, configFile) val proc = ProcessService() @@ -85,14 +85,12 @@ object PluginUtil { * * @param context The context to use. * @param config The profile configuration. - * @param domainPort The domain and port information. + * @param socksPort The port information. * @return The generated configuration file. */ - private fun genConfigHy2(context: Context, config: ProfileItem, domainPort: String?): File? { + private fun genConfigHy2(context: Context, config: ProfileItem, socksPort: Int): File? { Log.i(AppConfig.TAG, "runPlugin $HYSTERIA2") - val socksPort = domainPort?.split(":")?.last() - .let { if (it.isNullOrEmpty()) return null else it.toInt() } val hy2Config = Hysteria2Fmt.toNativeConfig(config, socksPort) ?: return null val configFile = File(context.noBackupFilesDir, "hy2_${SystemClock.elapsedRealtime()}.json") From 8890d9f004ea6ba74ef73e903b73742271c9b6c8 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Sat, 19 Apr 2025 11:38:02 +0800 Subject: [PATCH 24/81] Organize and optimize the code of V2rayConfigManager --- .../main/java/com/v2ray/ang/fmt/HttpFmt.kt | 2 +- .../java/com/v2ray/ang/fmt/Hysteria2Fmt.kt | 2 +- .../java/com/v2ray/ang/fmt/ShadowsocksFmt.kt | 2 +- .../main/java/com/v2ray/ang/fmt/SocksFmt.kt | 2 +- .../main/java/com/v2ray/ang/fmt/TrojanFmt.kt | 2 +- .../main/java/com/v2ray/ang/fmt/VlessFmt.kt | 2 +- .../main/java/com/v2ray/ang/fmt/VmessFmt.kt | 2 +- .../java/com/v2ray/ang/fmt/WireguardFmt.kt | 2 +- .../v2ray/ang/handler/V2rayConfigManager.kt | 491 +++++++++++------- .../main/java/com/v2ray/ang/util/HttpUtil.kt | 8 +- 10 files changed, 313 insertions(+), 202 deletions(-) diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/HttpFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/HttpFmt.kt index 4163737e..73def17e 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/HttpFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/HttpFmt.kt @@ -14,7 +14,7 @@ object HttpFmt : FmtBase() { * @return the converted OutboundBean object, or null if conversion fails */ fun toOutbound(profileItem: ProfileItem): OutboundBean? { - val outboundBean = V2rayConfigManager.createOutbound(EConfigType.HTTP) + val outboundBean = V2rayConfigManager.createInitOutbound(EConfigType.HTTP) outboundBean?.settings?.servers?.first()?.let { server -> server.address = profileItem.server.orEmpty() diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/Hysteria2Fmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/Hysteria2Fmt.kt index 80d127fb..dcc49f06 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/Hysteria2Fmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/Hysteria2Fmt.kt @@ -145,7 +145,7 @@ object Hysteria2Fmt : FmtBase() { * @return the converted OutboundBean object, or null if conversion fails */ fun toOutbound(profileItem: ProfileItem): OutboundBean? { - val outboundBean = V2rayConfigManager.createOutbound(EConfigType.HYSTERIA2) + val outboundBean = V2rayConfigManager.createInitOutbound(EConfigType.HYSTERIA2) return outboundBean } } \ No newline at end of file diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/ShadowsocksFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/ShadowsocksFmt.kt index e41e707e..7a5ac437 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/ShadowsocksFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/ShadowsocksFmt.kt @@ -132,7 +132,7 @@ object ShadowsocksFmt : FmtBase() { * @return the converted OutboundBean object, or null if conversion fails */ fun toOutbound(profileItem: ProfileItem): OutboundBean? { - val outboundBean = V2rayConfigManager.createOutbound(EConfigType.SHADOWSOCKS) + val outboundBean = V2rayConfigManager.createInitOutbound(EConfigType.SHADOWSOCKS) outboundBean?.settings?.servers?.first()?.let { server -> server.address = profileItem.server.orEmpty() diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt index 39013e4b..46046f48 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt @@ -61,7 +61,7 @@ object SocksFmt : FmtBase() { * @return the converted OutboundBean object, or null if conversion fails */ fun toOutbound(profileItem: ProfileItem): OutboundBean? { - val outboundBean = V2rayConfigManager.createOutbound(EConfigType.SOCKS) + val outboundBean = V2rayConfigManager.createInitOutbound(EConfigType.SOCKS) outboundBean?.settings?.servers?.first()?.let { server -> server.address = profileItem.server.orEmpty() diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt index efd83f9e..8b6c1dec 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt @@ -61,7 +61,7 @@ object TrojanFmt : FmtBase() { * @return the converted OutboundBean object, or null if conversion fails */ fun toOutbound(profileItem: ProfileItem): OutboundBean? { - val outboundBean = V2rayConfigManager.createOutbound(EConfigType.TROJAN) + val outboundBean = V2rayConfigManager.createInitOutbound(EConfigType.TROJAN) outboundBean?.settings?.servers?.first()?.let { server -> server.address = profileItem.server.orEmpty() diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VlessFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VlessFmt.kt index fe835ad3..2092843e 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VlessFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VlessFmt.kt @@ -57,7 +57,7 @@ object VlessFmt : FmtBase() { * @return the converted OutboundBean object, or null if conversion fails */ fun toOutbound(profileItem: ProfileItem): OutboundBean? { - val outboundBean = V2rayConfigManager.createOutbound(EConfigType.VLESS) + val outboundBean = V2rayConfigManager.createInitOutbound(EConfigType.VLESS) outboundBean?.settings?.vnext?.first()?.let { vnext -> vnext.address = profileItem.server.orEmpty() diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt index 002a75db..e3a72e61 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt @@ -169,7 +169,7 @@ object VmessFmt : FmtBase() { * @return the converted OutboundBean object, or null if conversion fails */ fun toOutbound(profileItem: ProfileItem): OutboundBean? { - val outboundBean = V2rayConfigManager.createOutbound(EConfigType.VMESS) + val outboundBean = V2rayConfigManager.createInitOutbound(EConfigType.VMESS) outboundBean?.settings?.vnext?.first()?.let { vnext -> vnext.address = profileItem.server.orEmpty() diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt index 10d4992a..30a33577 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt @@ -106,7 +106,7 @@ object WireguardFmt : FmtBase() { * @return the converted OutboundBean object, or null if conversion fails */ fun toOutbound(profileItem: ProfileItem): OutboundBean? { - val outboundBean = V2rayConfigManager.createOutbound(EConfigType.WIREGUARD) + val outboundBean = V2rayConfigManager.createInitOutbound(EConfigType.WIREGUARD) outboundBean?.settings?.let { wireguard -> wireguard.secretKey = profileItem.secretKey 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 e1ab5c4e..3ba5f0a8 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 @@ -4,32 +4,6 @@ import android.content.Context import android.text.TextUtils import android.util.Log import com.v2ray.ang.AppConfig -import com.v2ray.ang.AppConfig.DEFAULT_NETWORK -import com.v2ray.ang.AppConfig.DNS_ALIDNS_ADDRESSES -import com.v2ray.ang.AppConfig.DNS_ALIDNS_DOMAIN -import com.v2ray.ang.AppConfig.DNS_CLOUDFLARE_ADDRESSES -import com.v2ray.ang.AppConfig.DNS_CLOUDFLARE_DOMAIN -import com.v2ray.ang.AppConfig.DNS_DNSPOD_ADDRESSES -import com.v2ray.ang.AppConfig.DNS_DNSPOD_DOMAIN -import com.v2ray.ang.AppConfig.DNS_GOOGLE_ADDRESSES -import com.v2ray.ang.AppConfig.DNS_GOOGLE_DOMAIN -import com.v2ray.ang.AppConfig.DNS_QUAD9_ADDRESSES -import com.v2ray.ang.AppConfig.DNS_QUAD9_DOMAIN -import com.v2ray.ang.AppConfig.DNS_YANDEX_ADDRESSES -import com.v2ray.ang.AppConfig.DNS_YANDEX_DOMAIN -import com.v2ray.ang.AppConfig.GEOIP_CN -import com.v2ray.ang.AppConfig.GEOSITE_CN -import com.v2ray.ang.AppConfig.GEOSITE_PRIVATE -import com.v2ray.ang.AppConfig.GOOGLEAPIS_CN_DOMAIN -import com.v2ray.ang.AppConfig.GOOGLEAPIS_COM_DOMAIN -import com.v2ray.ang.AppConfig.HEADER_TYPE_HTTP -import com.v2ray.ang.AppConfig.LOOPBACK -import com.v2ray.ang.AppConfig.PROTOCOL_FREEDOM -import com.v2ray.ang.AppConfig.TAG_BLOCKED -import com.v2ray.ang.AppConfig.TAG_DIRECT -import com.v2ray.ang.AppConfig.TAG_FRAGMENT -import com.v2ray.ang.AppConfig.TAG_PROXY -import com.v2ray.ang.AppConfig.WIREGUARD_LOCAL_ADDRESS_V4 import com.v2ray.ang.dto.ConfigResult import com.v2ray.ang.dto.EConfigType import com.v2ray.ang.dto.NetworkType @@ -56,6 +30,8 @@ import com.v2ray.ang.util.Utils object V2rayConfigManager { private var initConfigCache: String? = null + //region get config function + /** * Retrieves the V2ray configuration for the given GUID. * @@ -133,30 +109,30 @@ object V2rayConfigManager { v2rayConfig.log.loglevel = MmkvManager.decodeSettingsString(AppConfig.PREF_LOGLEVEL) ?: "warning" v2rayConfig.remarks = config.remarks - inbounds(v2rayConfig) + getInbounds(v2rayConfig) if (config.configType == EConfigType.HYSTERIA2) { - result.socksPort = plusOutbound(v2rayConfig, config) ?: return result + result.socksPort = getPlusOutbounds(v2rayConfig, config) ?: return result } else { - outbounds(v2rayConfig, config) ?: return result - moreOutbounds(v2rayConfig, config.subscriptionId) + getOutbounds(v2rayConfig, config) ?: return result + getMoreOutbounds(v2rayConfig, config.subscriptionId) } - routing(v2rayConfig) + getRouting(v2rayConfig) - fakedns(v2rayConfig) + getFakeDns(v2rayConfig) - dns(v2rayConfig) + getDns(v2rayConfig) if (MmkvManager.decodeSettingsBool(AppConfig.PREF_LOCAL_DNS_ENABLED) == true) { - customLocalDns(v2rayConfig) + getCustomLocalDns(v2rayConfig) } if (MmkvManager.decodeSettingsBool(AppConfig.PREF_SPEED_ENABLED) != true) { v2rayConfig.stats = null v2rayConfig.policy = null } - resolveProxyDomainsToHosts(v2rayConfig) + resolveOutboundDomainsToHosts(v2rayConfig) result.status = true result.content = JsonUtil.toJsonPretty(v2rayConfig) ?: "" @@ -186,10 +162,10 @@ object V2rayConfigManager { val v2rayConfig = initV2rayConfig(context) ?: return result if (config.configType == EConfigType.HYSTERIA2) { - result.socksPort = plusOutbound(v2rayConfig, config) ?: return result + result.socksPort = getPlusOutbounds(v2rayConfig, config) ?: return result } else { - outbounds(v2rayConfig, config) ?: return result - moreOutbounds(v2rayConfig, config.subscriptionId) + getOutbounds(v2rayConfig, config) ?: return result + getMoreOutbounds(v2rayConfig, config.subscriptionId) } v2rayConfig.log.loglevel = MmkvManager.decodeSettingsString(AppConfig.PREF_LOGLEVEL) ?: "warning" @@ -220,7 +196,6 @@ object V2rayConfigManager { * @param context Android context used to access application assets * @return V2rayConfig object parsed from the JSON configuration, or null if the configuration is empty */ - private fun initV2rayConfig(context: Context): V2rayConfig? { val assets = initConfigCache ?: Utils.readTextFromAssets(context, "v2ray_config.json") if (TextUtils.isEmpty(assets)) { @@ -231,14 +206,28 @@ object V2rayConfigManager { return config } - private fun inbounds(v2rayConfig: V2rayConfig): Boolean { + + //endregion + + + //region some sub function + + /** + * Configures the inbound settings for V2ray. + * + * This function sets up the listening ports, sniffing options, and other inbound-related configurations. + * + * @param v2rayConfig The V2ray configuration object to be modified + * @return true if inbound configuration was successful, false otherwise + */ + private fun getInbounds(v2rayConfig: V2rayConfig): Boolean { try { val socksPort = SettingsManager.getSocksPort() v2rayConfig.inbounds.forEach { curInbound -> if (MmkvManager.decodeSettingsBool(AppConfig.PREF_PROXY_SHARING) != true) { //bind all inbounds to localhost if the user requests - curInbound.listen = LOOPBACK + curInbound.listen = AppConfig.LOOPBACK } } v2rayConfig.inbounds[0].port = socksPort @@ -269,22 +258,14 @@ object V2rayConfigManager { return true } - private fun outbounds(v2rayConfig: V2rayConfig, config: ProfileItem): Boolean? { - val outbound = getProxyOutbound(config) ?: return null - val ret = updateOutboundWithGlobalSettings(outbound) - if (!ret) return null - - if (v2rayConfig.outbounds.isNotEmpty()) { - v2rayConfig.outbounds[0] = outbound - } else { - v2rayConfig.outbounds.add(outbound) - } - - updateOutboundFragment(v2rayConfig) - return true - } - - private fun fakedns(v2rayConfig: V2rayConfig) { + /** + * Configures the fake DNS settings if enabled. + * + * Adds FakeDNS configuration to v2rayConfig if both local DNS and fake DNS are enabled. + * + * @param v2rayConfig The V2ray configuration object to be modified + */ + private fun getFakeDns(v2rayConfig: V2rayConfig) { if (MmkvManager.decodeSettingsBool(AppConfig.PREF_LOCAL_DNS_ENABLED) == true && MmkvManager.decodeSettingsBool(AppConfig.PREF_FAKE_DNS_ENABLED) == true ) { @@ -292,7 +273,15 @@ object V2rayConfigManager { } } - private fun routing(v2rayConfig: V2rayConfig): Boolean { + /** + * Configures routing settings for V2ray. + * + * Sets up the domain strategy and adds routing rules from saved rulesets. + * + * @param v2rayConfig The V2ray configuration object to be modified + * @return true if routing configuration was successful, false otherwise + */ + private fun getRouting(v2rayConfig: V2rayConfig): Boolean { try { v2rayConfig.routing.domainStrategy = @@ -301,7 +290,7 @@ object V2rayConfigManager { val rulesetItems = MmkvManager.decodeRoutingRulesets() rulesetItems?.forEach { key -> - routingUserRule(key, v2rayConfig) + getRoutingUserRule(key, v2rayConfig) } } catch (e: Exception) { Log.e(AppConfig.TAG, "Failed to configure routing", e) @@ -310,7 +299,13 @@ object V2rayConfigManager { return true } - private fun routingUserRule(item: RulesetItem?, v2rayConfig: V2rayConfig) { + /** + * Adds a specific ruleset item to the routing configuration. + * + * @param item The ruleset item to add + * @param v2rayConfig The V2ray configuration object to be modified + */ + private fun getRoutingUserRule(item: RulesetItem?, v2rayConfig: V2rayConfig) { try { if (item == null || !item.enabled) { return @@ -325,14 +320,22 @@ object V2rayConfigManager { } } - private fun userRule2Domain(tag: String): ArrayList { + /** + * Retrieves domain rules for a specific outbound tag. + * + * Searches through all rulesets to find domains targeting the specified tag. + * + * @param tag The outbound tag to search for + * @return ArrayList of domain rules matching the tag + */ + private fun getUserRule2Domain(tag: String): ArrayList { val domain = ArrayList() val rulesetItems = MmkvManager.decodeRoutingRulesets() rulesetItems?.forEach { key -> if (key.enabled && key.outboundTag == tag && !key.domain.isNullOrEmpty()) { key.domain?.forEach { - if (it != GEOSITE_PRIVATE + if (it != AppConfig.GEOSITE_PRIVATE && (it.startsWith("geosite:") || it.startsWith("domain:")) ) { domain.add(it) @@ -344,12 +347,20 @@ object V2rayConfigManager { return domain } - private fun customLocalDns(v2rayConfig: V2rayConfig): Boolean { + /** + * Configures custom local DNS settings. + * + * Sets up DNS inbound, outbound, and routing rules for local DNS resolution. + * + * @param v2rayConfig The V2ray configuration object to be modified + * @return true if custom local DNS configuration was successful, false otherwise + */ + private fun getCustomLocalDns(v2rayConfig: V2rayConfig): Boolean { try { if (MmkvManager.decodeSettingsBool(AppConfig.PREF_FAKE_DNS_ENABLED) == true) { - val geositeCn = arrayListOf(GEOSITE_CN) - val proxyDomain = userRule2Domain(TAG_PROXY) - val directDomain = userRule2Domain(TAG_DIRECT) + val geositeCn = arrayListOf(AppConfig.GEOSITE_CN) + val proxyDomain = getUserRule2Domain(AppConfig.TAG_PROXY) + val directDomain = getUserRule2Domain(AppConfig.TAG_DIRECT) // fakedns with all domains to make it always top priority v2rayConfig.dns?.servers?.add( 0, @@ -377,7 +388,7 @@ object V2rayConfigManager { V2rayConfig.InboundBean( tag = "dns-in", port = localDnsPort, - listen = LOOPBACK, + listen = AppConfig.LOOPBACK, protocol = "dokodemo-door", settings = dnsInboundSettings, sniffing = null @@ -413,14 +424,22 @@ object V2rayConfigManager { return true } - private fun dns(v2rayConfig: V2rayConfig): Boolean { + /** + * Configures the DNS settings for V2ray. + * + * Sets up DNS servers, hosts, and routing rules for DNS resolution. + * + * @param v2rayConfig The V2ray configuration object to be modified + * @return true if DNS configuration was successful, false otherwise + */ + private fun getDns(v2rayConfig: V2rayConfig): Boolean { try { val hosts = mutableMapOf() val servers = ArrayList() //remote Dns val remoteDns = SettingsManager.getRemoteDnsServers() - val proxyDomain = userRule2Domain(TAG_PROXY) + val proxyDomain = getUserRule2Domain(AppConfig.TAG_PROXY) remoteDns.forEach { servers.add(it) } @@ -435,9 +454,9 @@ object V2rayConfigManager { // domestic DNS val domesticDns = SettingsManager.getDomesticDnsServers() - val directDomain = userRule2Domain(TAG_DIRECT) - val isCnRoutingMode = directDomain.contains(GEOSITE_CN) - val geoipCn = arrayListOf(GEOIP_CN) + val directDomain = getUserRule2Domain(AppConfig.TAG_DIRECT) + val isCnRoutingMode = directDomain.contains(AppConfig.GEOSITE_CN) + val geoipCn = arrayListOf(AppConfig.GEOIP_CN) if (directDomain.isNotEmpty()) { servers.add( V2rayConfig.DnsBean.ServersBean( @@ -452,7 +471,7 @@ object V2rayConfigManager { if (Utils.isPureIpAddress(domesticDns.first())) { v2rayConfig.routing.rules.add( 0, RulesBean( - outboundTag = TAG_DIRECT, + outboundTag = AppConfig.TAG_DIRECT, port = "53", ip = arrayListOf(domesticDns.first()), domain = null @@ -475,21 +494,21 @@ object V2rayConfigManager { } //block dns - val blkDomain = userRule2Domain(TAG_BLOCKED) + val blkDomain = getUserRule2Domain(AppConfig.TAG_BLOCKED) if (blkDomain.isNotEmpty()) { - hosts.putAll(blkDomain.map { it to LOOPBACK }) + hosts.putAll(blkDomain.map { it to AppConfig.LOOPBACK }) } // hardcode googleapi rule to fix play store problems - hosts[GOOGLEAPIS_CN_DOMAIN] = GOOGLEAPIS_COM_DOMAIN + hosts[AppConfig.GOOGLEAPIS_CN_DOMAIN] = AppConfig.GOOGLEAPIS_COM_DOMAIN // hardcode popular Android Private DNS rule to fix localhost DNS problem - hosts[DNS_ALIDNS_DOMAIN] = DNS_ALIDNS_ADDRESSES - hosts[DNS_CLOUDFLARE_DOMAIN] = DNS_CLOUDFLARE_ADDRESSES - hosts[DNS_DNSPOD_DOMAIN] = DNS_DNSPOD_ADDRESSES - hosts[DNS_GOOGLE_DOMAIN] = DNS_GOOGLE_ADDRESSES - hosts[DNS_QUAD9_DOMAIN] = DNS_QUAD9_ADDRESSES - hosts[DNS_YANDEX_DOMAIN] = DNS_YANDEX_ADDRESSES + hosts[AppConfig.DNS_ALIDNS_DOMAIN] = AppConfig.DNS_ALIDNS_ADDRESSES + hosts[AppConfig.DNS_CLOUDFLARE_DOMAIN] = AppConfig.DNS_CLOUDFLARE_ADDRESSES + hosts[AppConfig.DNS_DNSPOD_DOMAIN] = AppConfig.DNS_DNSPOD_ADDRESSES + hosts[AppConfig.DNS_GOOGLE_DOMAIN] = AppConfig.DNS_GOOGLE_ADDRESSES + hosts[AppConfig.DNS_QUAD9_DOMAIN] = AppConfig.DNS_QUAD9_ADDRESSES + hosts[AppConfig.DNS_YANDEX_DOMAIN] = AppConfig.DNS_YANDEX_ADDRESSES // DNS dns @@ -502,7 +521,7 @@ object V2rayConfigManager { if (Utils.isPureIpAddress(remoteDns.first())) { v2rayConfig.routing.rules.add( 0, RulesBean( - outboundTag = TAG_PROXY, + outboundTag = AppConfig.TAG_PROXY, port = "53", ip = arrayListOf(remoteDns.first()), domain = null @@ -516,6 +535,138 @@ object V2rayConfigManager { return true } + + //endregion + + + //region outbound related functions + + /** + * Configures the primary outbound connection. + * + * Converts the profile to an outbound configuration and applies global settings. + * + * @param v2rayConfig The V2ray configuration object to be modified + * @param config The profile item containing connection details + * @return true if outbound configuration was successful, null if there was an error + */ + private fun getOutbounds(v2rayConfig: V2rayConfig, config: ProfileItem): Boolean? { + val outbound = convertProfile2Outbound(config) ?: return null + val ret = updateOutboundWithGlobalSettings(outbound) + if (!ret) return null + + if (v2rayConfig.outbounds.isNotEmpty()) { + v2rayConfig.outbounds[0] = outbound + } else { + v2rayConfig.outbounds.add(outbound) + } + + updateOutboundFragment(v2rayConfig) + return true + } + + /** + * Configures special outbound settings for Hysteria2 protocol. + * + * Creates a SOCKS outbound connection on a free port for protocols requiring special handling. + * + * @param v2rayConfig The V2ray configuration object to be modified + * @param config The profile item containing connection details + * @return The port number for the SOCKS connection, or null if there was an error + */ + private fun getPlusOutbounds(v2rayConfig: V2rayConfig, config: ProfileItem): Int? { + try { + val socksPort = Utils.findFreePort(listOf(100 + SettingsManager.getSocksPort(), 0)) + + val outboundNew = OutboundBean( + mux = null, + protocol = EConfigType.SOCKS.name.lowercase(), + settings = OutSettingsBean( + servers = listOf( + OutSettingsBean.ServersBean( + address = AppConfig.LOOPBACK, + port = socksPort + ) + ) + ) + ) + if (v2rayConfig.outbounds.isNotEmpty()) { + v2rayConfig.outbounds[0] = outboundNew + } else { + v2rayConfig.outbounds.add(outboundNew) + } + + return socksPort + } catch (e: Exception) { + Log.e(AppConfig.TAG, "Failed to configure plusOutbound", e) + return null + } + } + + /** + * Configures additional outbound connections for proxy chaining. + * + * Sets up previous and next proxies in a subscription for advanced routing capabilities. + * + * @param v2rayConfig The V2ray configuration object to be modified + * @param subscriptionId The subscription ID to look up related proxies + * @return true if additional outbounds were configured successfully, false otherwise + */ + private fun getMoreOutbounds(v2rayConfig: V2rayConfig, subscriptionId: String): Boolean { + //fragment proxy + if (MmkvManager.decodeSettingsBool(AppConfig.PREF_FRAGMENT_ENABLED, false) == true) { + return false + } + + if (subscriptionId.isEmpty()) { + return false + } + try { + val subItem = MmkvManager.decodeSubscription(subscriptionId) ?: return false + + //current proxy + val outbound = v2rayConfig.outbounds[0] + + //Previous proxy + val prevNode = SettingsManager.getServerViaRemarks(subItem.prevProfile) + if (prevNode != null) { + val prevOutbound = convertProfile2Outbound(prevNode) + if (prevOutbound != null) { + updateOutboundWithGlobalSettings(prevOutbound) + prevOutbound.tag = AppConfig.TAG_PROXY + "2" + v2rayConfig.outbounds.add(prevOutbound) + outbound.ensureSockopt().dialerProxy = prevOutbound.tag + } + } + + //Next proxy + val nextNode = SettingsManager.getServerViaRemarks(subItem.nextProfile) + if (nextNode != null) { + val nextOutbound = convertProfile2Outbound(nextNode) + if (nextOutbound != null) { + updateOutboundWithGlobalSettings(nextOutbound) + nextOutbound.tag = AppConfig.TAG_PROXY + v2rayConfig.outbounds.add(0, nextOutbound) + outbound.tag = AppConfig.TAG_PROXY + "1" + nextOutbound.ensureSockopt().dialerProxy = outbound.tag + } + } + } catch (e: Exception) { + Log.e(AppConfig.TAG, "Failed to configure more outbounds", e) + return false + } + + return true + } + + /** + * Updates outbound settings based on global preferences. + * + * Applies multiplexing and protocol-specific settings to an outbound connection. + * + * @param outbound The outbound connection to update + * @return true if the update was successful, false otherwise + */ private fun updateOutboundWithGlobalSettings(outbound: V2rayConfig.OutboundBean): Boolean { try { var muxEnabled = MmkvManager.decodeSettingsBool(AppConfig.PREF_MUX_ENABLED, false) @@ -547,7 +698,7 @@ object V2rayConfigManager { if (protocol.equals(EConfigType.WIREGUARD.name, true)) { var localTunAddr = if (outbound.settings?.address == null) { - listOf(WIREGUARD_LOCAL_ADDRESS_V4) + listOf(AppConfig.WIREGUARD_LOCAL_ADDRESS_V4) } else { outbound.settings?.address as List<*> } @@ -557,8 +708,8 @@ object V2rayConfigManager { outbound.settings?.address = localTunAddr } - if (outbound.streamSettings?.network == DEFAULT_NETWORK - && outbound.streamSettings?.tcpSettings?.header?.type == HEADER_TYPE_HTTP + if (outbound.streamSettings?.network == AppConfig.DEFAULT_NETWORK + && outbound.streamSettings?.tcpSettings?.header?.type == AppConfig.HEADER_TYPE_HTTP ) { val path = outbound.streamSettings?.tcpSettings?.header?.request?.path val host = outbound.streamSettings?.tcpSettings?.header?.request?.headers?.Host @@ -568,7 +719,7 @@ object V2rayConfigManager { } outbound.streamSettings?.tcpSettings?.header?.request = JsonUtil.fromJson( requestString, - V2rayConfig.OutboundBean.StreamSettingsBean.TcpSettingsBean.HeaderBean.RequestBean::class.java + StreamSettingsBean.TcpSettingsBean.HeaderBean.RequestBean::class.java ) outbound.streamSettings?.tcpSettings?.header?.request?.path = if (path.isNullOrEmpty()) { @@ -587,6 +738,14 @@ object V2rayConfigManager { return true } + /** + * Updates the outbound with fragment settings for traffic optimization. + * + * Configures packet fragmentation for TLS and REALITY protocols if enabled. + * + * @param v2rayConfig The V2ray configuration object to be modified + * @return true if fragment configuration was successful, false otherwise + */ private fun updateOutboundFragment(v2rayConfig: V2rayConfig): Boolean { try { if (MmkvManager.decodeSettingsBool(AppConfig.PREF_FRAGMENT_ENABLED, false) == false) { @@ -600,8 +759,8 @@ object V2rayConfigManager { val fragmentOutbound = V2rayConfig.OutboundBean( - protocol = PROTOCOL_FREEDOM, - tag = TAG_FRAGMENT, + protocol = AppConfig.PROTOCOL_FREEDOM, + tag = AppConfig.TAG_FRAGMENT, mux = null ) @@ -617,8 +776,8 @@ object V2rayConfigManager { packets = "tlshello" } - fragmentOutbound.settings = V2rayConfig.OutboundBean.OutSettingsBean( - fragment = V2rayConfig.OutboundBean.OutSettingsBean.FragmentBean( + fragmentOutbound.settings = OutboundBean.OutSettingsBean( + fragment = OutboundBean.OutSettingsBean.FragmentBean( packets = packets, length = MmkvManager.decodeSettingsString(AppConfig.PREF_FRAGMENT_LENGTH) ?: "50-100", @@ -626,15 +785,15 @@ object V2rayConfigManager { ?: "10-20" ), noises = listOf( - V2rayConfig.OutboundBean.OutSettingsBean.NoiseBean( + OutboundBean.OutSettingsBean.NoiseBean( type = "rand", packet = "10-20", delay = "10-16", ) ), ) - fragmentOutbound.streamSettings = V2rayConfig.OutboundBean.StreamSettingsBean( - sockopt = V2rayConfig.OutboundBean.StreamSettingsBean.SockoptBean( + fragmentOutbound.streamSettings = StreamSettingsBean( + sockopt = StreamSettingsBean.SockoptBean( TcpNoDelay = true, mark = 255 ) @@ -643,8 +802,8 @@ object V2rayConfigManager { //proxy chain v2rayConfig.outbounds[0].streamSettings?.sockopt = - V2rayConfig.OutboundBean.StreamSettingsBean.SockoptBean( - dialerProxy = TAG_FRAGMENT + StreamSettingsBean.SockoptBean( + dialerProxy = AppConfig.TAG_FRAGMENT ) } catch (e: Exception) { Log.e(AppConfig.TAG, "Failed to update outbound fragment", e) @@ -653,106 +812,28 @@ object V2rayConfigManager { return true } - private fun moreOutbounds(v2rayConfig: V2rayConfig, subscriptionId: String): Boolean { - //fragment proxy - if (MmkvManager.decodeSettingsBool(AppConfig.PREF_FRAGMENT_ENABLED, false) == true) { - return false - } - - if (subscriptionId.isEmpty()) { - return false - } - try { - val subItem = MmkvManager.decodeSubscription(subscriptionId) ?: return false - - //current proxy - val outbound = v2rayConfig.outbounds[0] - - //Previous proxy - val prevNode = SettingsManager.getServerViaRemarks(subItem.prevProfile) - if (prevNode != null) { - val prevOutbound = getProxyOutbound(prevNode) - if (prevOutbound != null) { - updateOutboundWithGlobalSettings(prevOutbound) - prevOutbound.tag = TAG_PROXY + "2" - v2rayConfig.outbounds.add(prevOutbound) - outbound.ensureSockopt().dialerProxy = prevOutbound.tag - } - } - - //Next proxy - val nextNode = SettingsManager.getServerViaRemarks(subItem.nextProfile) - if (nextNode != null) { - val nextOutbound = getProxyOutbound(nextNode) - if (nextOutbound != null) { - updateOutboundWithGlobalSettings(nextOutbound) - nextOutbound.tag = TAG_PROXY - v2rayConfig.outbounds.add(0, nextOutbound) - outbound.tag = TAG_PROXY + "1" - nextOutbound.ensureSockopt().dialerProxy = outbound.tag - } - } - } catch (e: Exception) { - Log.e(AppConfig.TAG, "Failed to configure more outbounds", e) - return false - } - - return true - } - - private fun plusOutbound(v2rayConfig: V2rayConfig, config: ProfileItem): Int? { - try { - val socksPort = Utils.findFreePort(listOf(100 + SettingsManager.getSocksPort(), 0)) - - val outboundNew = V2rayConfig.OutboundBean( - mux = null, - protocol = EConfigType.SOCKS.name.lowercase(), - settings = V2rayConfig.OutboundBean.OutSettingsBean( - servers = listOf( - V2rayConfig.OutboundBean.OutSettingsBean.ServersBean( - address = LOOPBACK, - port = socksPort - ) - ) - ) - ) - if (v2rayConfig.outbounds.isNotEmpty()) { - v2rayConfig.outbounds[0] = outboundNew - } else { - v2rayConfig.outbounds.add(outboundNew) - } - - return socksPort - } catch (e: Exception) { - Log.e(AppConfig.TAG, "Failed to configure plusOutbound", e) - return null - } - } - - private fun resolveProxyDomainsToHosts(v2rayConfig: V2rayConfig) { + /** + * Resolves domain names to IP addresses in outbound connections. + * + * Pre-resolves domains to improve connection speed and reliability. + * + * @param v2rayConfig The V2ray configuration object to be modified + */ + private fun resolveOutboundDomainsToHosts(v2rayConfig: V2rayConfig) { val proxyOutboundList = v2rayConfig.getAllProxyOutbound() val dns = v2rayConfig.dns ?: return - val newHosts = dns.hosts?.toMutableMap() ?: mutableMapOf() - val preferIpv6 = MmkvManager.decodeSettingsBool(AppConfig.PREF_PREFER_IPV6) == true for (item in proxyOutboundList) { val domain = item.getServerAddress() if (domain.isNullOrEmpty()) continue - if (newHosts.containsKey(domain)) continue - val resolvedIps = HttpUtil.resolveHostToIP( - domain, - preferIpv6 - ) - - if (resolvedIps.isEmpty()) continue - - item.ensureSockopt().domainStrategy = - if (preferIpv6) "UseIPv6v4" else "UseIPv4v6" + val resolvedIps = HttpUtil.resolveHostToIP(domain, preferIpv6) + if (resolvedIps.isNullOrEmpty()) continue + item.ensureSockopt().domainStrategy = if (preferIpv6) "UseIPv6v4" else "UseIPv4v6" newHosts[domain] = if (resolvedIps.size == 1) { resolvedIps[0] } else { @@ -764,12 +845,14 @@ object V2rayConfigManager { } /** - * Retrieves the proxy outbound configuration for the given profile item. + * Converts a profile item to an outbound configuration. * - * @param profileItem The profile item for which to get the proxy outbound configuration. - * @return The proxy outbound configuration as a V2rayConfig.OutboundBean, or null if not found. + * Creates appropriate outbound settings based on the protocol type. + * + * @param profileItem The profile item to convert + * @return OutboundBean configuration for the profile, or null if not supported */ - private fun getProxyOutbound(profileItem: ProfileItem): V2rayConfig.OutboundBean? { + private fun convertProfile2Outbound(profileItem: ProfileItem): V2rayConfig.OutboundBean? { return when (profileItem.configType) { EConfigType.VMESS -> VmessFmt.toOutbound(profileItem) EConfigType.CUSTOM -> null @@ -783,7 +866,15 @@ object V2rayConfigManager { } } - fun createOutbound(configType: EConfigType): OutboundBean? { + /** + * Creates an initial outbound configuration for a specific protocol type. + * + * Provides a template configuration for different protocol types. + * + * @param configType The type of configuration to create + * @return An initial OutboundBean for the specified configuration type, or null for custom types + */ + fun createInitOutbound(configType: EConfigType): OutboundBean? { return when (configType) { EConfigType.VMESS, EConfigType.VLESS -> @@ -825,6 +916,15 @@ object V2rayConfigManager { } } + /** + * Configures transport settings for an outbound connection. + * + * Sets up protocol-specific transport options based on the profile settings. + * + * @param streamSettings The stream settings to configure + * @param profileItem The profile containing transport configuration + * @return The Server Name Indication (SNI) value to use, or null if not applicable + */ fun populateTransportSettings(streamSettings: StreamSettingsBean, profileItem: ProfileItem): String? { val transport = profileItem.network.orEmpty() val headerType = profileItem.headerType @@ -933,6 +1033,15 @@ object V2rayConfigManager { return sni } + /** + * Configures TLS or REALITY security settings for an outbound connection. + * + * Sets up security-related parameters like certificates, fingerprints, and SNI. + * + * @param streamSettings The stream settings to configure + * @param profileItem The profile containing security configuration + * @param sniExt An external SNI value to use if the profile doesn't specify one + */ fun populateTlsSettings(streamSettings: StreamSettingsBean, profileItem: ProfileItem, sniExt: String?) { val streamSecurity = profileItem.security.orEmpty() val allowInsecure = profileItem.insecure == true @@ -962,4 +1071,6 @@ object V2rayConfigManager { streamSettings.realitySettings = tlsSetting } } + + //endregion } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt index 371fa509..9e08f663 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt @@ -41,17 +41,17 @@ object HttpUtil { * @param ipv6Preferred Whether to prefer IPv6 addresses, defaults to false * @return The resolved IP address or the original input (if it's already an IP or resolution fails) */ - fun resolveHostToIP(host: String, ipv6Preferred: Boolean = false): List { + fun resolveHostToIP(host: String, ipv6Preferred: Boolean = false): List? { try { // If it's already an IP address, return it as a list if (Utils.isPureIpAddress(host)) { - return listOf(host) + return null } // Get all IP addresses val addresses = InetAddress.getAllByName(host) if (addresses.isEmpty()) { - return emptyList() + return null } // Sort addresses based on preference @@ -68,7 +68,7 @@ object HttpUtil { return ipList } catch (e: Exception) { Log.e(AppConfig.TAG, "Failed to resolve host to IP", e) - return emptyList() + return null } } From df5ea251e1072901043eec437bb4c3fd9c398854 Mon Sep 17 00:00:00 2001 From: patterniha <71074308+patterniha@users.noreply.github.com> Date: Sat, 19 Apr 2025 11:05:20 +0330 Subject: [PATCH 25/81] move Prefer_IPv6 settings (#4507) --- V2rayNG/app/src/main/res/values/strings.xml | 2 +- V2rayNG/app/src/main/res/xml/pref_settings.xml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/V2rayNG/app/src/main/res/values/strings.xml b/V2rayNG/app/src/main/res/values/strings.xml index c1b31042..b5b44c38 100644 --- a/V2rayNG/app/src/main/res/values/strings.xml +++ b/V2rayNG/app/src/main/res/values/strings.xml @@ -174,7 +174,7 @@ Local DNS returns fake IP addresses (faster, but it may not work for some apps) Prefer IPv6 - Prefer IPv6 addresses and routes + Enable IPv6 routes and Prefer IPv6 addresses Remote DNS (udp/tcp/https/quic)(Optional) DNS diff --git a/V2rayNG/app/src/main/res/xml/pref_settings.xml b/V2rayNG/app/src/main/res/xml/pref_settings.xml index 8a5f9040..75cad848 100644 --- a/V2rayNG/app/src/main/res/xml/pref_settings.xml +++ b/V2rayNG/app/src/main/res/xml/pref_settings.xml @@ -19,6 +19,11 @@ android:title="@string/title_pref_is_booted" /> + + - - Date: Sun, 20 Apr 2025 05:01:50 +0330 Subject: [PATCH 26/81] Update Persian translate (#4518) https://github.com/2dust/v2rayNG/pull/4507 --- V2rayNG/app/src/main/res/values-fa/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/V2rayNG/app/src/main/res/values-fa/strings.xml b/V2rayNG/app/src/main/res/values-fa/strings.xml index 7b81716c..0ffc8bd3 100644 --- a/V2rayNG/app/src/main/res/values-fa/strings.xml +++ b/V2rayNG/app/src/main/res/values-fa/strings.xml @@ -171,7 +171,7 @@ دی ان اس محلی آدرس های آیپی جعلی را بر می گرداند (سریع تر می باشد و تاخیر را کاهش می دهد اما ممکن است برای برخی از برنامه ها کار نکند) ترجیح دادن IPV6 - ترجیح دادن نشانی و مسیر های IPv6 + مسیرهای IPv6 را فعال کنید و آدرس‌های IPv6 را ترجیح دهید DNS از راه دور (اختیاری) (udp/tcp/https/quic) DNS From c130d55e8fbbc96c81cb2015379649b76c7761be Mon Sep 17 00:00:00 2001 From: AmirHossein Abdolmotallebi Date: Sat, 19 Apr 2025 18:32:52 -0700 Subject: [PATCH 27/81] Update V2rayConfig.kt (#4522) --- V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt index 0d08df47..155be104 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt @@ -23,8 +23,8 @@ data class V2rayConfig( ) { data class LogBean( - val access: String, - val error: String, + val access: String? = null, + val error: String? = null, var loglevel: String? = null, val dnsLog: Boolean? = null ) @@ -553,4 +553,4 @@ data class V2rayConfig( EConfigType.entries.any { it.name.equals(outbound.protocol, ignoreCase = true) } } } -} \ No newline at end of file +} From 72da42ee40307da94f7c26f16593cffefabf5544 Mon Sep 17 00:00:00 2001 From: Hossein Abaspanah <63148255+hosseinabaspanah@users.noreply.github.com> Date: Sun, 20 Apr 2025 05:03:01 +0330 Subject: [PATCH 28/81] Update Luri Bakhtiari translation (#4524) --- V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 1805f237..b686b341 100644 --- a/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml +++ b/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml @@ -40,7 +40,7 @@ نیشتنا نشۊوی پورت - نوم من توری + نوم منتوری شناسه جایگۊزین ٱمنیت شبکه @@ -73,7 +73,7 @@ رزم ٱمنیت رزم (اختیاری) - نوم من توری (اختیاری) + نوم منتوری (اختیاری) رزم نگاری جریان کیلیت پوی وولاتی @@ -173,7 +173,7 @@ DNS مهلی نشۊویا IP جئلی ن وورگنه (زل تر، ٱما گاشڌ من یقرد ز برنومه یل کار نکونه) ترجی IPv6 - ترجی داڌن نشۊوی وو تورا IPv6 + تورا IPv6 ن فعال کۊنین وو نشۊویا IPv6 ن ترجی بڌین DNS ز ر دیر (اختیاری) (udp/tcp/https/quic) (اختیاری) DNS From 41fd2b0cfbc32918a67b5858e11c8844bb02d65f Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Tue, 22 Apr 2025 10:36:10 +0800 Subject: [PATCH 29/81] Fix --- .../src/main/java/com/v2ray/ang/service/NotificationService.kt | 1 + .../src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/NotificationService.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/NotificationService.kt index cb9ad811..92c551a6 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/NotificationService.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/NotificationService.kt @@ -158,6 +158,7 @@ object NotificationService { mBuilder = null speedNotificationJob?.cancel() speedNotificationJob = null + mNotificationManager = null } /** diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt index 95ab22cd..659158a5 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt @@ -169,8 +169,8 @@ object V2RayServiceManager { try { MessageUtil.sendMsg2UI(service, AppConfig.MSG_STATE_START_SUCCESS, "") - NotificationService.startSpeedNotification(currentConfig) NotificationService.showNotification(currentConfig) + NotificationService.startSpeedNotification(currentConfig) PluginUtil.runPlugin(service, config, result.socksPort) } catch (e: Exception) { From 247e2b3ba3149ea3dcb2c3f10603c95f0371f8b1 Mon Sep 17 00:00:00 2001 From: solokot Date: Tue, 22 Apr 2025 05:36:23 +0300 Subject: [PATCH 30/81] Update Russian translation (#4532) --- V2rayNG/app/src/main/res/values-ru/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/V2rayNG/app/src/main/res/values-ru/strings.xml b/V2rayNG/app/src/main/res/values-ru/strings.xml index 168d29da..ab94b307 100644 --- a/V2rayNG/app/src/main/res/values-ru/strings.xml +++ b/V2rayNG/app/src/main/res/values-ru/strings.xml @@ -172,7 +172,7 @@ Локальная DNS возвращает поддельные IP-адреса (быстрее, но может не работать с некоторыми приложениями) Предпочитать IPv6 - Предпочитать IPv6-адреса и маршрутизацию + Использовать маршрутизацию IPv6 предпочитать IPv6-адреса Удалённая DNS (UDP/TCP/HTTPS/QUIC) (необязательно) DNS @@ -302,7 +302,7 @@ Проверить подключение Проверка… Проверка профилей (%d) - Успешно: HTTP-соединение заняло %d мс + Успешно: соединение заняло %d мс Сбой проверки интернет-соединения: %s Интернет недоступен Код ошибки: #%d From 1bca321d3fcbe7304648e5465da5afb72695dece Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Wed, 23 Apr 2025 10:05:49 +0800 Subject: [PATCH 31/81] Temporarily add option to allow insecure HTTP subscription address https://github.com/2dust/v2rayNG/issues/4526 --- .../com/v2ray/ang/dto/SubscriptionItem.kt | 1 + .../com/v2ray/ang/handler/AngConfigManager.kt | 6 +++-- .../java/com/v2ray/ang/ui/SubEditActivity.kt | 6 ++++- .../src/main/res/layout/activity_sub_edit.xml | 22 +++++++++++++++++++ .../app/src/main/res/values-ar/strings.xml | 1 + .../app/src/main/res/values-bn/strings.xml | 1 + .../src/main/res/values-bqi-rIR/strings.xml | 1 + .../app/src/main/res/values-fa/strings.xml | 1 + .../app/src/main/res/values-ru/strings.xml | 1 + .../app/src/main/res/values-vi/strings.xml | 1 + .../src/main/res/values-zh-rCN/strings.xml | 1 + .../src/main/res/values-zh-rTW/strings.xml | 1 + V2rayNG/app/src/main/res/values/strings.xml | 1 + 13 files changed, 41 insertions(+), 3 deletions(-) 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 8e8c66a4..8957df78 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,5 +11,6 @@ data class SubscriptionItem( var prevProfile: String? = null, var nextProfile: String? = null, var filter: String? = null, + var allowInsecureUrl: Boolean = false, ) 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 449a05d0..1dcd1276 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 @@ -419,8 +419,10 @@ object AngConfigManager { if (!Utils.isValidUrl(url)) { return 0 } - if (!Utils.isValidSubUrl(url)) { - return 0 + if (!it.second.allowInsecureUrl) { + if (!Utils.isValidSubUrl(url)) { + return 0 + } } Log.i(AppConfig.TAG, url) 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 649156fb..fff8f42d 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,6 +46,7 @@ class SubEditActivity : BaseActivity() { binding.etFilter.text = Utils.getEditable(subItem.filter) binding.chkEnable.isChecked = subItem.enabled binding.autoUpdateCheck.isChecked = subItem.autoUpdate + binding.allowInsecureUrl.isChecked = subItem.allowInsecureUrl binding.etPreProfile.text = Utils.getEditable(subItem.prevProfile) binding.etNextProfile.text = Utils.getEditable(subItem.nextProfile) return true @@ -77,6 +78,7 @@ class SubEditActivity : BaseActivity() { subItem.autoUpdate = binding.autoUpdateCheck.isChecked subItem.prevProfile = binding.etPreProfile.text.toString() subItem.nextProfile = binding.etNextProfile.text.toString() + subItem.allowInsecureUrl = binding.allowInsecureUrl.isChecked if (TextUtils.isEmpty(subItem.remarks)) { toast(R.string.sub_setting_remarks) @@ -90,7 +92,9 @@ class SubEditActivity : BaseActivity() { if (!Utils.isValidSubUrl(subItem.url)) { toast(R.string.toast_insecure_url_protocol) - return false + if (!subItem.allowInsecureUrl) { + return false + } } } 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 28575fd1..af5d70cd 100644 --- a/V2rayNG/app/src/main/res/layout/activity_sub_edit.xml +++ b/V2rayNG/app/src/main/res/layout/activity_sub_edit.xml @@ -138,6 +138,28 @@ + + + + + + + + Remarks regular filter تفعيل التحديث تفعيل التحديث التلقائي + Allow insecure HTTP address Previous proxy configuration remarks Next proxy configuration remarks The configuration remarks exists and is unique diff --git a/V2rayNG/app/src/main/res/values-bn/strings.xml b/V2rayNG/app/src/main/res/values-bn/strings.xml index 58f22b17..4520f2e2 100644 --- a/V2rayNG/app/src/main/res/values-bn/strings.xml +++ b/V2rayNG/app/src/main/res/values-bn/strings.xml @@ -258,6 +258,7 @@ Remarks regular filter আপডেট সক্রিয় করুন স্বয়ংক্রিয় আপডেট সক্রিয় করুন + Allow insecure HTTP address Previous proxy configuration remarks Next proxy configuration remarks The configuration remarks exists and is unique 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 b686b341..d37ca571 100644 --- a/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml +++ b/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml @@ -258,6 +258,7 @@ نوم موستعار فیلتر فعال بیڌن ورۊ کردن فعال بیڌن ورۊ کردن خوتکار + Allow insecure HTTP address نوم موستعار پروکسی دیندایی نوم موستعار پروکسی نیایی موتمعن بۊ ک نوم موستعار هڌس وو جۊرس نی diff --git a/V2rayNG/app/src/main/res/values-fa/strings.xml b/V2rayNG/app/src/main/res/values-fa/strings.xml index 0ffc8bd3..46146082 100644 --- a/V2rayNG/app/src/main/res/values-fa/strings.xml +++ b/V2rayNG/app/src/main/res/values-fa/strings.xml @@ -255,6 +255,7 @@ نام مستعار فیلتر فعال کردن به‌روزرسانی فعال سازی به‌روزرسانی خودکار + Allow insecure HTTP address نام مستعار پروکسی قبلی نام مستعار پروکسی بعدی لطفاً مطمئن شوید که نام مستعار وجود دارد و منحصر به فرد است diff --git a/V2rayNG/app/src/main/res/values-ru/strings.xml b/V2rayNG/app/src/main/res/values-ru/strings.xml index ab94b307..807f88fa 100644 --- a/V2rayNG/app/src/main/res/values-ru/strings.xml +++ b/V2rayNG/app/src/main/res/values-ru/strings.xml @@ -257,6 +257,7 @@ Название фильтра Использовать обновление Использовать автообновление + Allow insecure HTTP address Предыдущая конфигурация прокси Следующая конфигурация прокси Конфигурация должна быть уникальной diff --git a/V2rayNG/app/src/main/res/values-vi/strings.xml b/V2rayNG/app/src/main/res/values-vi/strings.xml index 296f039e..ac247ce0 100644 --- a/V2rayNG/app/src/main/res/values-vi/strings.xml +++ b/V2rayNG/app/src/main/res/values-vi/strings.xml @@ -258,6 +258,7 @@ Remarks regular filter Sử dụng gói đăng ký này Bật tự động cập nhật + Allow insecure HTTP address Previous proxy configuration remarks Next proxy configuration remarks The configuration remarks exists and is unique 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 b7bef7c3..38303d49 100644 --- a/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml +++ b/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml @@ -255,6 +255,7 @@ 别名正则过滤 启用更新 启用自动更新 + 允许不安全的 HTTP 地址 前置代理配置文件别名 落地代理配置文件別名 请确保配置文件别名存在并唯一 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 bb7403df..959312e9 100644 --- a/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml +++ b/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml @@ -256,6 +256,7 @@ 別名正規過濾 啟用更新 啟用自動更新 + 允許不安全的 HTTP 位址 前置代理設定檔别名 落地代理設定檔別名 请确保設定檔别名存在并唯一 diff --git a/V2rayNG/app/src/main/res/values/strings.xml b/V2rayNG/app/src/main/res/values/strings.xml index b5b44c38..069dcfc7 100644 --- a/V2rayNG/app/src/main/res/values/strings.xml +++ b/V2rayNG/app/src/main/res/values/strings.xml @@ -259,6 +259,7 @@ Remarks regular filter Enable update Enable automatic update + Allow insecure HTTP address Previous proxy configuration remarks Next proxy configuration remarks The configuration remarks exists and is unique From ef1bb3dd34cac57913b49370c1ac4ba9482cf612 Mon Sep 17 00:00:00 2001 From: Pk-web6936 <202365630+Pk-web6936@users.noreply.github.com> Date: Thu, 24 Apr 2025 05:41:02 +0330 Subject: [PATCH 32/81] Update Persian translation (#4533) --- V2rayNG/app/src/main/res/values-fa/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/V2rayNG/app/src/main/res/values-fa/strings.xml b/V2rayNG/app/src/main/res/values-fa/strings.xml index 46146082..1341f182 100644 --- a/V2rayNG/app/src/main/res/values-fa/strings.xml +++ b/V2rayNG/app/src/main/res/values-fa/strings.xml @@ -255,7 +255,7 @@ نام مستعار فیلتر فعال کردن به‌روزرسانی فعال سازی به‌روزرسانی خودکار - Allow insecure HTTP address + مجاز کردن آدرس HTTP ناامن نام مستعار پروکسی قبلی نام مستعار پروکسی بعدی لطفاً مطمئن شوید که نام مستعار وجود دارد و منحصر به فرد است From 0fc1f2f5d30baf8f8508d3943bd2ade5a24ca500 Mon Sep 17 00:00:00 2001 From: solokot Date: Thu, 24 Apr 2025 05:11:10 +0300 Subject: [PATCH 33/81] Update Russian translation (#4534) --- V2rayNG/app/src/main/res/values-ru/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/V2rayNG/app/src/main/res/values-ru/strings.xml b/V2rayNG/app/src/main/res/values-ru/strings.xml index 807f88fa..cc56161b 100644 --- a/V2rayNG/app/src/main/res/values-ru/strings.xml +++ b/V2rayNG/app/src/main/res/values-ru/strings.xml @@ -257,7 +257,7 @@ Название фильтра Использовать обновление Использовать автообновление - Allow insecure HTTP address + Разрешать незащищённые HTTP-адреса Предыдущая конфигурация прокси Следующая конфигурация прокси Конфигурация должна быть уникальной From cdb9b1811cb9bfd59f8b05b4318ea50616f6ae23 Mon Sep 17 00:00:00 2001 From: Hossein Abaspanah <63148255+hosseinabaspanah@users.noreply.github.com> Date: Thu, 24 Apr 2025 05:41:20 +0330 Subject: [PATCH 34/81] Update Luri Bakhtiari translation (#4535) --- V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 d37ca571..45dc5158 100644 --- a/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml +++ b/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml @@ -258,7 +258,7 @@ نوم موستعار فیلتر فعال بیڌن ورۊ کردن فعال بیڌن ورۊ کردن خوتکار - Allow insecure HTTP address + موجاز کردن نشۊوی HTTP نا ٱمن نوم موستعار پروکسی دیندایی نوم موستعار پروکسی نیایی موتمعن بۊ ک نوم موستعار هڌس وو جۊرس نی From 06aa680d4574ab19ee60c2fa8389f74fe244daf5 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Thu, 24 Apr 2025 10:26:03 +0800 Subject: [PATCH 35/81] Update libs.versions.toml --- V2rayNG/gradle/libs.versions.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/V2rayNG/gradle/libs.versions.toml b/V2rayNG/gradle/libs.versions.toml index 6a107010..2c4a5d98 100644 --- a/V2rayNG/gradle/libs.versions.toml +++ b/V2rayNG/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -agp = "8.9.1" +agp = "8.9.2" desugarJdkLibs = "2.1.5" gradleLicensePlugin = "0.9.8" kotlin = "2.1.20" @@ -20,7 +20,7 @@ swiperefreshlayout = "1.1.0" toasty = "1.5.2" editorkit = "2.9.0" core = "3.5.3" -workRuntimeKtx = "2.10.0" +workRuntimeKtx = "2.10.1" lifecycleViewmodelKtx = "2.8.7" multidex = "2.0.1" mockitoMockitoInline = "5.2.0" From 5b79951da75a9e736b87f5a5a7fbbe906449d23e Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Thu, 24 Apr 2025 10:33:23 +0800 Subject: [PATCH 36/81] up 1.10.0 --- V2rayNG/app/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/V2rayNG/app/build.gradle.kts b/V2rayNG/app/build.gradle.kts index d97ae358..7478730e 100644 --- a/V2rayNG/app/build.gradle.kts +++ b/V2rayNG/app/build.gradle.kts @@ -12,8 +12,8 @@ android { applicationId = "com.v2ray.ang" minSdk = 21 targetSdk = 35 - versionCode = 646 - versionName = "1.9.46" + versionCode = 650 + versionName = "1.10.0" multiDexEnabled = true val abiFilterList = (properties["ABI_FILTERS"] as? String)?.split(';') From 2fdf684ee7198d7f896df4e1f536f3c213ea5ba3 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Mon, 28 Apr 2025 15:30:16 +0800 Subject: [PATCH 37/81] Fix https://github.com/2dust/v2rayNG/issues/4548 --- V2rayNG/app/src/main/assets/v2ray_config.json | 2 +- .../src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/V2rayNG/app/src/main/assets/v2ray_config.json b/V2rayNG/app/src/main/assets/v2ray_config.json index 90abcee0..4f8c3d7e 100644 --- a/V2rayNG/app/src/main/assets/v2ray_config.json +++ b/V2rayNG/app/src/main/assets/v2ray_config.json @@ -97,7 +97,7 @@ } ], "routing": { - "domainStrategy": "IPIfNonMatch", + "domainStrategy": "AsIs", "rules": [] }, "dns": { 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 3ba5f0a8..2e06f823 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 @@ -286,7 +286,7 @@ object V2rayConfigManager { v2rayConfig.routing.domainStrategy = MmkvManager.decodeSettingsString(AppConfig.PREF_ROUTING_DOMAIN_STRATEGY) - ?: "IPIfNonMatch" + ?: "AsIs" val rulesetItems = MmkvManager.decodeRoutingRulesets() rulesetItems?.forEach { key -> From 9bedfe8a7b0d49eb5cda9811a7b53f62a49980d2 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Wed, 30 Apr 2025 08:31:51 +0800 Subject: [PATCH 38/81] Bug fix https://github.com/2dust/v2rayNG/issues/4555 --- V2rayNG/app/src/main/java/com/v2ray/ang/fmt/Hysteria2Fmt.kt | 2 +- V2rayNG/app/src/main/java/com/v2ray/ang/fmt/ShadowsocksFmt.kt | 2 +- V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt | 2 +- V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt | 2 +- V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VlessFmt.kt | 2 +- V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt | 2 +- V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt | 2 +- .../app/src/main/java/com/v2ray/ang/handler/SettingsManager.kt | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/Hysteria2Fmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/Hysteria2Fmt.kt index dcc49f06..3b3dc88c 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/Hysteria2Fmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/Hysteria2Fmt.kt @@ -25,7 +25,7 @@ object Hysteria2Fmt : FmtBase() { val config = ProfileItem.create(EConfigType.HYSTERIA2) val uri = URI(Utils.fixIllegalUrl(str)) - config.remarks = Utils.urlDecode(uri.fragment.orEmpty()) + config.remarks = Utils.urlDecode(uri.fragment.orEmpty()).let { if (it.isEmpty()) "none" else it } config.server = uri.idnHost config.serverPort = uri.port.toString() config.password = uri.userInfo diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/ShadowsocksFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/ShadowsocksFmt.kt index 7a5ac437..172716c9 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/ShadowsocksFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/ShadowsocksFmt.kt @@ -36,7 +36,7 @@ object ShadowsocksFmt : FmtBase() { if (uri.port <= 0) return null if (uri.userInfo.isNullOrEmpty()) return null - config.remarks = Utils.urlDecode(uri.fragment.orEmpty()) + config.remarks = Utils.urlDecode(uri.fragment.orEmpty()).let { if (it.isEmpty()) "none" else it } config.server = uri.idnHost config.serverPort = uri.port.toString() diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt index 46046f48..6b727025 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt @@ -23,7 +23,7 @@ object SocksFmt : FmtBase() { if (uri.idnHost.isEmpty()) return null if (uri.port <= 0) return null - config.remarks = Utils.urlDecode(uri.fragment.orEmpty()) + config.remarks = Utils.urlDecode(uri.fragment.orEmpty()).let { if (it.isEmpty()) "none" else it } config.server = uri.idnHost config.serverPort = uri.port.toString() diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt index 8b6c1dec..6ad0d71b 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt @@ -23,7 +23,7 @@ object TrojanFmt : FmtBase() { val config = ProfileItem.create(EConfigType.TROJAN) val uri = URI(Utils.fixIllegalUrl(str)) - config.remarks = Utils.urlDecode(uri.fragment.orEmpty()) + config.remarks = Utils.urlDecode(uri.fragment.orEmpty()).let { if (it.isEmpty()) "none" else it } config.server = uri.idnHost config.serverPort = uri.port.toString() config.password = uri.userInfo diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VlessFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VlessFmt.kt index 2092843e..f5cf9fff 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VlessFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VlessFmt.kt @@ -26,7 +26,7 @@ object VlessFmt : FmtBase() { if (uri.rawQuery.isNullOrEmpty()) return null val queryParam = getQueryParam(uri) - config.remarks = Utils.urlDecode(uri.fragment.orEmpty()) + config.remarks = Utils.urlDecode(uri.fragment.orEmpty()).let { if (it.isEmpty()) "none" else it } config.server = uri.idnHost config.serverPort = uri.port.toString() config.password = uri.userInfo diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt index e3a72e61..c733f3dc 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt @@ -151,7 +151,7 @@ object VmessFmt : FmtBase() { if (uri.rawQuery.isNullOrEmpty()) return null val queryParam = getQueryParam(uri) - config.remarks = Utils.urlDecode(uri.fragment.orEmpty()) + config.remarks = Utils.urlDecode(uri.fragment.orEmpty()).let { if (it.isEmpty()) "none" else it } config.server = uri.idnHost config.serverPort = uri.port.toString() config.password = uri.userInfo diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt index 30a33577..8f1cec84 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt @@ -25,7 +25,7 @@ object WireguardFmt : FmtBase() { if (uri.rawQuery.isNullOrEmpty()) return null val queryParam = getQueryParam(uri) - config.remarks = Utils.urlDecode(uri.fragment.orEmpty()) + config.remarks = Utils.urlDecode(uri.fragment.orEmpty()).let { if (it.isEmpty()) "none" else it } config.server = uri.idnHost config.serverPort = uri.port.toString() diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SettingsManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SettingsManager.kt index cb364531..c528aeb2 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SettingsManager.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SettingsManager.kt @@ -216,7 +216,7 @@ object SettingsManager { * @return The ProfileItem. */ fun getServerViaRemarks(remarks: String?): ProfileItem? { - if (remarks == null) { + if (remarks.isNullOrEmpty()) { return null } val serverList = decodeServerList() From af04bbcf87789ce7525822a20ecc07a19adbc704 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Wed, 30 Apr 2025 14:35:48 +0800 Subject: [PATCH 39/81] up 1.10.1 --- V2rayNG/app/build.gradle.kts | 4 ++-- .../src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/V2rayNG/app/build.gradle.kts b/V2rayNG/app/build.gradle.kts index 7478730e..82b9ef6e 100644 --- a/V2rayNG/app/build.gradle.kts +++ b/V2rayNG/app/build.gradle.kts @@ -12,8 +12,8 @@ android { applicationId = "com.v2ray.ang" minSdk = 21 targetSdk = 35 - versionCode = 650 - versionName = "1.10.0" + versionCode = 651 + versionName = "1.10.1" multiDexEnabled = true val abiFilterList = (properties["ABI_FILTERS"] as? String)?.split(';') 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 2e06f823..8ef7a7b7 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 @@ -861,7 +861,7 @@ object V2rayConfigManager { EConfigType.VLESS -> VlessFmt.toOutbound(profileItem) EConfigType.TROJAN -> TrojanFmt.toOutbound(profileItem) EConfigType.WIREGUARD -> WireguardFmt.toOutbound(profileItem) - EConfigType.HYSTERIA2 -> Hysteria2Fmt.toOutbound(profileItem) + EConfigType.HYSTERIA2 -> null EConfigType.HTTP -> HttpFmt.toOutbound(profileItem) } } From 42c27a5e7ebc153ff82cf853c5f5de8cfb0a7cea Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Wed, 30 Apr 2025 14:48:59 +0800 Subject: [PATCH 40/81] Update hysteria --- hysteria | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hysteria b/hysteria index 245c6e9b..2adeec29 160000 --- a/hysteria +++ b/hysteria @@ -1 +1 @@ -Subproject commit 245c6e9bd17b1ef644f81fc4dafd0a1e1933da85 +Subproject commit 2adeec2900a7a0e3689f118580174cc528f9995a From 02e53ced50efd0654b52cfda749ae958eacf2870 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Wed, 30 Apr 2025 14:49:02 +0800 Subject: [PATCH 41/81] Update AndroidLibXrayLite --- AndroidLibXrayLite | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AndroidLibXrayLite b/AndroidLibXrayLite index c53ff63a..bafb45ff 160000 --- a/AndroidLibXrayLite +++ b/AndroidLibXrayLite @@ -1 +1 @@ -Subproject commit c53ff63a3be2583d4b90f6819dfb0266cada0e18 +Subproject commit bafb45ffcf3b5fb3c8bf8d3afb88aa95b1bc1613 From 71a5b6e4805e3ce66afaeaa88c423c54e596acf4 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Sun, 4 May 2025 17:49:02 +0800 Subject: [PATCH 42/81] Update AndroidLibXrayLite --- AndroidLibXrayLite | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AndroidLibXrayLite b/AndroidLibXrayLite index bafb45ff..ae953fdc 160000 --- a/AndroidLibXrayLite +++ b/AndroidLibXrayLite @@ -1 +1 @@ -Subproject commit bafb45ffcf3b5fb3c8bf8d3afb88aa95b1bc1613 +Subproject commit ae953fdc4da7766e50ffc553c213edd735f38af4 From 7f9cb8dfddf598d6d3f411e9b4943c5a94d881fd Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Wed, 7 May 2025 10:14:14 +0800 Subject: [PATCH 43/81] Check upgrade function is visible --- .../main/java/com/v2ray/ang/ui/AboutActivity.kt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/AboutActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/AboutActivity.kt index 7c2ffdba..dfd26352 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/AboutActivity.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/AboutActivity.kt @@ -106,13 +106,13 @@ class AboutActivity : BaseActivity() { } //If it is the Google Play version, not be displayed within 1 days after update - if (Utils.isGoogleFlavor()) { - val lastUpdateTime = AppManagerUtil.getLastUpdateTime(this) - val currentTime = System.currentTimeMillis() - if ((currentTime - lastUpdateTime) < 1 * 24 * 60 * 60 * 1000L) { - binding.layoutCheckUpdate.visibility = View.GONE - } - } +// if (Utils.isGoogleFlavor()) { +// val lastUpdateTime = AppManagerUtil.getLastUpdateTime(this) +// val currentTime = System.currentTimeMillis() +// if ((currentTime - lastUpdateTime) < 1 * 24 * 60 * 60 * 1000L) { +// binding.layoutCheckUpdate.visibility = View.GONE +// } +// } binding.layoutCheckUpdate.setOnClickListener { checkForUpdates(binding.checkPreRelease.isChecked) } From be0a2506ceda592bea674fd76eaa0d2cd806287d Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Wed, 7 May 2025 10:44:19 +0800 Subject: [PATCH 44/81] Update AndroidLibXrayLite --- AndroidLibXrayLite | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AndroidLibXrayLite b/AndroidLibXrayLite index ae953fdc..5cdcbc61 160000 --- a/AndroidLibXrayLite +++ b/AndroidLibXrayLite @@ -1 +1 @@ -Subproject commit ae953fdc4da7766e50ffc553c213edd735f38af4 +Subproject commit 5cdcbc611f5df8f33cb2040de5369f368e099e3e From 3773962b64b300b3a40cdd620d6f0f37d8b52bcd Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Wed, 7 May 2025 10:47:50 +0800 Subject: [PATCH 45/81] up 1.10.2 --- V2rayNG/app/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/V2rayNG/app/build.gradle.kts b/V2rayNG/app/build.gradle.kts index 82b9ef6e..2e04a87f 100644 --- a/V2rayNG/app/build.gradle.kts +++ b/V2rayNG/app/build.gradle.kts @@ -12,8 +12,8 @@ android { applicationId = "com.v2ray.ang" minSdk = 21 targetSdk = 35 - versionCode = 651 - versionName = "1.10.1" + versionCode = 652 + versionName = "1.10.2" multiDexEnabled = true val abiFilterList = (properties["ABI_FILTERS"] as? String)?.split(';') From d447adc97fc24472e4d32213c7f1490b72698785 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Sun, 11 May 2025 18:07:26 +0800 Subject: [PATCH 46/81] Fix https://github.com/2dust/v2rayN/discussions/7268 --- V2rayNG/app/src/main/java/com/v2ray/ang/AppConfig.kt | 2 +- V2rayNG/app/src/main/java/com/v2ray/ang/dto/IPAPIInfo.kt | 9 +++++---- .../main/java/com/v2ray/ang/handler/SpeedtestManager.kt | 7 +++++-- 3 files changed, 11 insertions(+), 7 deletions(-) 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 649b71c1..9e1b7918 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/AppConfig.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/AppConfig.kt @@ -103,7 +103,7 @@ object AppConfig { const val TG_CHANNEL_URL = "https://t.me/github_2dust" const val DELAY_TEST_URL = "https://www.gstatic.com/generate_204" const val DELAY_TEST_URL2 = "https://www.google.com/generate_204" - const val IP_API_Url = "https://api.ip.sb/geoip" + const val IP_API_URL = "https://speed.cloudflare.com/meta" /** DNS server addresses. */ const val DNS_PROXY = "1.1.1.1" diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/IPAPIInfo.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/IPAPIInfo.kt index 4d29ffc7..97814fbb 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/IPAPIInfo.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/IPAPIInfo.kt @@ -2,10 +2,11 @@ package com.v2ray.ang.dto data class IPAPIInfo( var ip: String? = null, - var city: String? = null, - var region: String? = null, - var region_code: String? = null, + var clientIp: String? = null, + var ip_addr: String? = null, + var query: String? = null, var country: String? = null, var country_name: String? = null, - var country_code: String? = null + var country_code: String? = null, + var countryCode: String? = null ) \ No newline at end of file diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SpeedtestManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SpeedtestManager.kt index 99898a3a..e547c378 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SpeedtestManager.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SpeedtestManager.kt @@ -168,10 +168,13 @@ object SpeedtestManager { fun getRemoteIPInfo(): String? { val httpPort = SettingsManager.getHttpPort() - var content = HttpUtil.getUrlContent(AppConfig.IP_API_Url, 5000, httpPort) ?: return null + var content = HttpUtil.getUrlContent(AppConfig.IP_API_URL, 5000, httpPort) ?: return null var ipInfo = JsonUtil.fromJson(content, IPAPIInfo::class.java) ?: return null - return "(${ipInfo.country_code}) ${ipInfo.ip}" + var ip = ipInfo.ip ?: ipInfo.clientIp ?: ipInfo.ip_addr ?: ipInfo.query + var country = ipInfo.country_code ?: ipInfo.country ?: ipInfo.countryCode + + return "(${country ?: "unknown"}) $ip" } /** From 4a87549fa76f6a08c8aba408d1e12d25d7350c44 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Thu, 15 May 2025 10:58:52 +0800 Subject: [PATCH 47/81] Update README.md --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index 83214e02..4f068ffb 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,6 @@ A V2Ray client for Android, support [Xray core](https://github.com/XTLS/Xray-cor [![GitHub Releases](https://img.shields.io/github/downloads/2dust/v2rayNG/latest/total?logo=github)](https://github.com/2dust/v2rayNG/releases) [![Chat on Telegram](https://img.shields.io/badge/Chat%20on-Telegram-brightgreen.svg)](https://t.me/v2rayn) - -Get it on Google Play - - ### Telegram Channel [github_2dust](https://t.me/github_2dust) From f22454da5dd52399ae19f525436b1ac8628b0fe0 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Sat, 17 May 2025 11:48:15 +0800 Subject: [PATCH 48/81] Update AndroidLibXrayLite --- AndroidLibXrayLite | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AndroidLibXrayLite b/AndroidLibXrayLite index 5cdcbc61..ddcaecad 160000 --- a/AndroidLibXrayLite +++ b/AndroidLibXrayLite @@ -1 +1 @@ -Subproject commit 5cdcbc611f5df8f33cb2040de5369f368e099e3e +Subproject commit ddcaecad0ae2f0816b33b8a56d1e36fce4efcae4 From 55bc2bf934eff286203545f66aa9e32c7216b308 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Sat, 17 May 2025 12:01:34 +0800 Subject: [PATCH 49/81] up 1.10.3 --- V2rayNG/app/build.gradle.kts | 4 ++-- V2rayNG/gradle/libs.versions.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/V2rayNG/app/build.gradle.kts b/V2rayNG/app/build.gradle.kts index 2e04a87f..f2c34017 100644 --- a/V2rayNG/app/build.gradle.kts +++ b/V2rayNG/app/build.gradle.kts @@ -12,8 +12,8 @@ android { applicationId = "com.v2ray.ang" minSdk = 21 targetSdk = 35 - versionCode = 652 - versionName = "1.10.2" + versionCode = 653 + versionName = "1.10.3" multiDexEnabled = true val abiFilterList = (properties["ABI_FILTERS"] as? String)?.split(';') diff --git a/V2rayNG/gradle/libs.versions.toml b/V2rayNG/gradle/libs.versions.toml index 2c4a5d98..8c46149e 100644 --- a/V2rayNG/gradle/libs.versions.toml +++ b/V2rayNG/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -agp = "8.9.2" +agp = "8.9.3" desugarJdkLibs = "2.1.5" gradleLicensePlugin = "0.9.8" kotlin = "2.1.20" @@ -21,7 +21,7 @@ toasty = "1.5.2" editorkit = "2.9.0" core = "3.5.3" workRuntimeKtx = "2.10.1" -lifecycleViewmodelKtx = "2.8.7" +lifecycleViewmodelKtx = "2.9.0" multidex = "2.0.1" mockitoMockitoInline = "5.2.0" flexbox = "3.0.0" From e6f260da76e8843668b0bb031c1e7c991fc57902 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Fri, 23 May 2025 14:34:55 +0800 Subject: [PATCH 50/81] Added the check update entry to the main interface drawer menu https://github.com/2dust/v2rayNG/issues/4599 --- V2rayNG/app/src/main/AndroidManifest.xml | 3 + .../java/com/v2ray/ang/ui/AboutActivity.kt | 48 ----------- .../com/v2ray/ang/ui/CheckUpdateActivity.kt | 70 ++++++++++++++++ .../java/com/v2ray/ang/ui/MainActivity.kt | 1 + .../src/main/res/layout/activity_about.xml | 43 ---------- .../main/res/layout/activity_check_update.xml | 82 +++++++++++++++++++ V2rayNG/app/src/main/res/menu/menu_drawer.xml | 4 + .../app/src/main/res/values-ar/strings.xml | 1 + .../app/src/main/res/values-bn/strings.xml | 1 + .../src/main/res/values-bqi-rIR/strings.xml | 1 + .../app/src/main/res/values-fa/strings.xml | 1 + .../app/src/main/res/values-ru/strings.xml | 1 + .../app/src/main/res/values-vi/strings.xml | 1 + .../src/main/res/values-zh-rCN/strings.xml | 1 + .../src/main/res/values-zh-rTW/strings.xml | 1 + V2rayNG/app/src/main/res/values/strings.xml | 1 + 16 files changed, 169 insertions(+), 91 deletions(-) create mode 100644 V2rayNG/app/src/main/java/com/v2ray/ang/ui/CheckUpdateActivity.kt create mode 100644 V2rayNG/app/src/main/res/layout/activity_check_update.xml diff --git a/V2rayNG/app/src/main/AndroidManifest.xml b/V2rayNG/app/src/main/AndroidManifest.xml index 4ff08af9..00e4b747 100644 --- a/V2rayNG/app/src/main/AndroidManifest.xml +++ b/V2rayNG/app/src/main/AndroidManifest.xml @@ -144,6 +144,9 @@ + diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/AboutActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/AboutActivity.kt index dfd26352..1931cb45 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/AboutActivity.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/AboutActivity.kt @@ -5,28 +5,21 @@ import android.content.Intent import android.os.Build import android.os.Bundle import android.util.Log -import android.view.View import androidx.activity.result.contract.ActivityResultContracts -import androidx.appcompat.app.AlertDialog import androidx.core.content.ContextCompat import androidx.core.content.FileProvider -import androidx.lifecycle.lifecycleScope import com.tencent.mmkv.MMKV import com.v2ray.ang.AppConfig import com.v2ray.ang.BuildConfig import com.v2ray.ang.R import com.v2ray.ang.databinding.ActivityAboutBinding -import com.v2ray.ang.dto.CheckUpdateResult import com.v2ray.ang.extension.toast import com.v2ray.ang.extension.toastError import com.v2ray.ang.extension.toastSuccess import com.v2ray.ang.handler.MmkvManager import com.v2ray.ang.handler.SpeedtestManager -import com.v2ray.ang.handler.UpdateCheckerManager -import com.v2ray.ang.util.AppManagerUtil import com.v2ray.ang.util.Utils import com.v2ray.ang.util.ZipUtil -import kotlinx.coroutines.launch import java.io.File import java.text.SimpleDateFormat import java.util.Locale @@ -105,23 +98,6 @@ class AboutActivity : BaseActivity() { } } - //If it is the Google Play version, not be displayed within 1 days after update -// if (Utils.isGoogleFlavor()) { -// val lastUpdateTime = AppManagerUtil.getLastUpdateTime(this) -// val currentTime = System.currentTimeMillis() -// if ((currentTime - lastUpdateTime) < 1 * 24 * 60 * 60 * 1000L) { -// binding.layoutCheckUpdate.visibility = View.GONE -// } -// } - binding.layoutCheckUpdate.setOnClickListener { - checkForUpdates(binding.checkPreRelease.isChecked) - } - - binding.checkPreRelease.setOnCheckedChangeListener { _, isChecked -> - MmkvManager.encodeSettings(AppConfig.PREF_CHECK_UPDATE_PRE_RELEASE, isChecked) - } - binding.checkPreRelease.isChecked = MmkvManager.decodeSettingsBool(AppConfig.PREF_CHECK_UPDATE_PRE_RELEASE, false) - binding.layoutSoureCcode.setOnClickListener { Utils.openUri(this, AppConfig.APP_URL) } @@ -222,28 +198,4 @@ class AboutActivity : BaseActivity() { } } } - - private fun checkForUpdates(includePreRelease: Boolean) { - lifecycleScope.launch { - val result = UpdateCheckerManager.checkForUpdate(includePreRelease) - if (result.hasUpdate) { - showUpdateDialog(result) - } else { - toast(R.string.update_already_latest_version) - } - } - } - - private fun showUpdateDialog(result: CheckUpdateResult) { - AlertDialog.Builder(this) - .setTitle(getString(R.string.update_new_version_found, result.latestVersion)) - .setMessage(result.releaseNotes) - .setPositiveButton(R.string.update_now) { _, _ -> - result.downloadUrl?.let { - Utils.openUri(this, it) - } - } - .setNegativeButton(android.R.string.cancel, null) - .show() - } } \ No newline at end of file diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/CheckUpdateActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/CheckUpdateActivity.kt new file mode 100644 index 00000000..8f464a49 --- /dev/null +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/CheckUpdateActivity.kt @@ -0,0 +1,70 @@ +package com.v2ray.ang.ui + +import android.os.Bundle +import androidx.appcompat.app.AlertDialog +import androidx.lifecycle.lifecycleScope +import com.v2ray.ang.AppConfig +import com.v2ray.ang.BuildConfig +import com.v2ray.ang.R +import com.v2ray.ang.databinding.ActivityCheckUpdateBinding +import com.v2ray.ang.dto.CheckUpdateResult +import com.v2ray.ang.extension.toast +import com.v2ray.ang.extension.toastSuccess +import com.v2ray.ang.handler.MmkvManager +import com.v2ray.ang.handler.SpeedtestManager +import com.v2ray.ang.handler.UpdateCheckerManager +import com.v2ray.ang.util.Utils +import kotlinx.coroutines.launch + +class CheckUpdateActivity : BaseActivity() { + + private val binding by lazy { ActivityCheckUpdateBinding.inflate(layoutInflater) } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(binding.root) + + title = getString(R.string.update_check_for_update) + + binding.layoutCheckUpdate.setOnClickListener { + checkForUpdates(binding.checkPreRelease.isChecked) + } + + binding.checkPreRelease.setOnCheckedChangeListener { _, isChecked -> + MmkvManager.encodeSettings(AppConfig.PREF_CHECK_UPDATE_PRE_RELEASE, isChecked) + } + binding.checkPreRelease.isChecked = MmkvManager.decodeSettingsBool(AppConfig.PREF_CHECK_UPDATE_PRE_RELEASE, false) + + "v${BuildConfig.VERSION_NAME} (${SpeedtestManager.getLibVersion()})".also { + binding.tvVersion.text = it + } + + checkForUpdates(binding.checkPreRelease.isChecked) + } + + private fun checkForUpdates(includePreRelease: Boolean) { + toast(R.string.update_checking_for_update) + + lifecycleScope.launch { + val result = UpdateCheckerManager.checkForUpdate(includePreRelease) + if (result.hasUpdate) { + showUpdateDialog(result) + } else { + toastSuccess(R.string.update_already_latest_version) + } + } + } + + private fun showUpdateDialog(result: CheckUpdateResult) { + AlertDialog.Builder(this) + .setTitle(getString(R.string.update_new_version_found, result.latestVersion)) + .setMessage(result.releaseNotes) + .setPositiveButton(R.string.update_now) { _, _ -> + result.downloadUrl?.let { + Utils.openUri(this, it) + } + } + .setNegativeButton(android.R.string.cancel, null) + .show() + } +} \ No newline at end of file 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 bb9abc68..0c7584d8 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 @@ -685,6 +685,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList R.id.promotion -> Utils.openUri(this, "${Utils.decode(AppConfig.APP_PROMOTION_URL)}?t=${System.currentTimeMillis()}") R.id.logcat -> startActivity(Intent(this, LogcatActivity::class.java)) + R.id.check_for_update -> startActivity(Intent(this, CheckUpdateActivity::class.java)) R.id.about -> startActivity(Intent(this, AboutActivity::class.java)) } diff --git a/V2rayNG/app/src/main/res/layout/activity_about.xml b/V2rayNG/app/src/main/res/layout/activity_about.xml index d4596963..62053559 100644 --- a/V2rayNG/app/src/main/res/layout/activity_about.xml +++ b/V2rayNG/app/src/main/res/layout/activity_about.xml @@ -111,49 +111,6 @@ android:orientation="vertical" android:paddingTop="@dimen/padding_spacing_dp16"> - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/V2rayNG/app/src/main/res/menu/menu_drawer.xml b/V2rayNG/app/src/main/res/menu/menu_drawer.xml index c134759d..4e204e62 100644 --- a/V2rayNG/app/src/main/res/menu/menu_drawer.xml +++ b/V2rayNG/app/src/main/res/menu/menu_drawer.xml @@ -35,6 +35,10 @@ android:id="@+id/logcat" android:icon="@drawable/ic_logcat_24dp" android:title="@string/title_logcat" /> + New version found: %s Update now Check Pre-release + Checking for update… رمز استجابة سريعة (QRcode) diff --git a/V2rayNG/app/src/main/res/values-bn/strings.xml b/V2rayNG/app/src/main/res/values-bn/strings.xml index 4520f2e2..bd7c492f 100644 --- a/V2rayNG/app/src/main/res/values-bn/strings.xml +++ b/V2rayNG/app/src/main/res/values-bn/strings.xml @@ -315,6 +315,7 @@ New version found: %s Update now Check Pre-release + Checking for update… QR কোড 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 45dc5158..73a89fa3 100644 --- a/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml +++ b/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml @@ -324,6 +324,7 @@ نوسخه نۊ ن جوست: %s سکو ورۊ رسۊوی کۊنین واجۊری نوسخه یل پؽش ز تیجنیڌن + Checking for update… QRcode diff --git a/V2rayNG/app/src/main/res/values-fa/strings.xml b/V2rayNG/app/src/main/res/values-fa/strings.xml index 1341f182..b5455fbb 100644 --- a/V2rayNG/app/src/main/res/values-fa/strings.xml +++ b/V2rayNG/app/src/main/res/values-fa/strings.xml @@ -321,6 +321,7 @@ نسخه جدید پیدا شد: %s اکنون به روز رسانی کنید بررسی نسخه پیش از انتشار + Checking for update… QRcode diff --git a/V2rayNG/app/src/main/res/values-ru/strings.xml b/V2rayNG/app/src/main/res/values-ru/strings.xml index cc56161b..615a33d8 100644 --- a/V2rayNG/app/src/main/res/values-ru/strings.xml +++ b/V2rayNG/app/src/main/res/values-ru/strings.xml @@ -323,6 +323,7 @@ Найдена новая версия: %s Обновить Искать предварительный выпуск + Checking for update… QR-код diff --git a/V2rayNG/app/src/main/res/values-vi/strings.xml b/V2rayNG/app/src/main/res/values-vi/strings.xml index ac247ce0..72cdee64 100644 --- a/V2rayNG/app/src/main/res/values-vi/strings.xml +++ b/V2rayNG/app/src/main/res/values-vi/strings.xml @@ -317,6 +317,7 @@ New version found: %s Update now Check Pre-release + Checking for update… Xuất ra mã QR (Chụp màn hình để lưu) 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 38303d49..a0aa7a5a 100644 --- a/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml +++ b/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml @@ -315,6 +315,7 @@ 发现新版本: %s 立即更新 检查 Pre-release + 正在检查更新中… 二维码 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 959312e9..e655e18f 100644 --- a/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml +++ b/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml @@ -315,6 +315,7 @@ 發現新版本: %s 立即更新 檢查 Pre-release + 正在檢查更新中… QR Code diff --git a/V2rayNG/app/src/main/res/values/strings.xml b/V2rayNG/app/src/main/res/values/strings.xml index 069dcfc7..39defc59 100644 --- a/V2rayNG/app/src/main/res/values/strings.xml +++ b/V2rayNG/app/src/main/res/values/strings.xml @@ -325,6 +325,7 @@ New version found: %s Update now Check Pre-release + Checking for update… QRcode From f3f2b7fab51ef4aeaa92396c67849c016f57cb34 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Fri, 23 May 2025 16:17:38 +0800 Subject: [PATCH 51/81] Added delete function to subscription group list, secondary confirmation with settings --- .../java/com/v2ray/ang/ui/SubEditActivity.kt | 30 ++-- .../v2ray/ang/ui/SubSettingRecyclerAdapter.kt | 35 ++++ .../res/layout/item_recycler_sub_setting.xml | 165 +++++++++++------- 3 files changed, 161 insertions(+), 69 deletions(-) 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 fff8f42d..f85382f1 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 @@ -6,6 +6,7 @@ import android.view.Menu import android.view.MenuItem import androidx.appcompat.app.AlertDialog import androidx.lifecycle.lifecycleScope +import com.v2ray.ang.AppConfig import com.v2ray.ang.R import com.v2ray.ang.databinding.ActivitySubEditBinding import com.v2ray.ang.dto.SubscriptionItem @@ -109,19 +110,28 @@ class SubEditActivity : BaseActivity() { */ private fun deleteServer(): Boolean { if (editSubId.isNotEmpty()) { - AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm) - .setPositiveButton(android.R.string.ok) { _, _ -> - lifecycleScope.launch(Dispatchers.IO) { - MmkvManager.removeSubscription(editSubId) - launch(Dispatchers.Main) { - finish() + if (MmkvManager.decodeSettingsBool(AppConfig.PREF_CONFIRM_REMOVE) == true) { + AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm) + .setPositiveButton(android.R.string.ok) { _, _ -> + lifecycleScope.launch(Dispatchers.IO) { + MmkvManager.removeSubscription(editSubId) + launch(Dispatchers.Main) { + finish() + } } } + .setNegativeButton(android.R.string.cancel) { _, _ -> + // do nothing + } + .show() + } else { + lifecycleScope.launch(Dispatchers.IO) { + MmkvManager.removeSubscription(editSubId) + launch(Dispatchers.Main) { + finish() + } } - .setNegativeButton(android.R.string.cancel) { _, _ -> - // do nothing - } - .show() + } } return true } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/SubSettingRecyclerAdapter.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/SubSettingRecyclerAdapter.kt index bb364c1b..cc2d5404 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/SubSettingRecyclerAdapter.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/SubSettingRecyclerAdapter.kt @@ -8,6 +8,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.appcompat.app.AlertDialog +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.RecyclerView import com.v2ray.ang.AppConfig import com.v2ray.ang.R @@ -20,6 +21,8 @@ import com.v2ray.ang.helper.ItemTouchHelperAdapter import com.v2ray.ang.helper.ItemTouchHelperViewHolder import com.v2ray.ang.util.QRCodeDecoder import com.v2ray.ang.util.Utils +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch class SubSettingRecyclerAdapter(val activity: SubSettingActivity) : RecyclerView.Adapter(), ItemTouchHelperAdapter { @@ -46,6 +49,10 @@ class SubSettingRecyclerAdapter(val activity: SubSettingActivity) : RecyclerView ) } + holder.itemSubSettingBinding.layoutRemove.setOnClickListener { + removeSubscription(subId, position) + } + holder.itemSubSettingBinding.chkEnable.setOnCheckedChangeListener { it, isChecked -> if (!it.isPressed) return@setOnCheckedChangeListener subItem.enabled = isChecked @@ -54,9 +61,11 @@ class SubSettingRecyclerAdapter(val activity: SubSettingActivity) : RecyclerView } if (TextUtils.isEmpty(subItem.url)) { + holder.itemSubSettingBinding.layoutUrl.visibility = View.GONE holder.itemSubSettingBinding.layoutShare.visibility = View.INVISIBLE holder.itemSubSettingBinding.chkEnable.visibility = View.INVISIBLE } else { + holder.itemSubSettingBinding.layoutUrl.visibility = View.VISIBLE holder.itemSubSettingBinding.layoutShare.visibility = View.VISIBLE holder.itemSubSettingBinding.chkEnable.visibility = View.VISIBLE holder.itemSubSettingBinding.layoutShare.setOnClickListener { @@ -90,6 +99,32 @@ class SubSettingRecyclerAdapter(val activity: SubSettingActivity) : RecyclerView } } + private fun removeSubscription(subId: String, position: Int) { + if (MmkvManager.decodeSettingsBool(AppConfig.PREF_CONFIRM_REMOVE) == true) { + AlertDialog.Builder(mActivity).setMessage(R.string.del_config_comfirm) + .setPositiveButton(android.R.string.ok) { _, _ -> + removeSubscriptionSub(subId, position) + } + .setNegativeButton(android.R.string.cancel) { _, _ -> + //do noting + } + .show() + } else { + removeSubscriptionSub(subId, position) + } + } + + private fun removeSubscriptionSub(subId: String, position: Int) { + mActivity.lifecycleScope.launch(Dispatchers.IO) { + MmkvManager.removeSubscription(subId) + launch(Dispatchers.Main) { + notifyItemRemoved(position) + notifyItemRangeChanged(position, mActivity.subscriptions.size) + mActivity.refreshData() + } + } + } + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainViewHolder { return MainViewHolder( ItemRecyclerSubSettingBinding.inflate( diff --git a/V2rayNG/app/src/main/res/layout/item_recycler_sub_setting.xml b/V2rayNG/app/src/main/res/layout/item_recycler_sub_setting.xml index aa8d9812..47c2b4c5 100644 --- a/V2rayNG/app/src/main/res/layout/item_recycler_sub_setting.xml +++ b/V2rayNG/app/src/main/res/layout/item_recycler_sub_setting.xml @@ -15,97 +15,144 @@ android:clickable="true" android:focusable="true" android:gravity="center" - android:nextFocusRight="@+id/layout_edit" + android:nextFocusRight="@+id/layout_share" android:orientation="horizontal" android:padding="@dimen/padding_spacing_dp8"> - - - - - - - - + android:orientation="vertical"> + android:paddingStart="@dimen/padding_spacing_dp8"> - + + android:orientation="horizontal"> + + + + + + + + + + + + + + + + + + - + + android:orientation="horizontal" + android:paddingStart="@dimen/padding_spacing_dp8" + android:paddingEnd="@dimen/padding_spacing_dp8"> - + + + + + + + android:layout_marginTop="@dimen/padding_spacing_dp8" + android:orientation="horizontal"> + + + + + + - + \ No newline at end of file From 7e6b1c247bc6e5b9bf33f9acfee74cf8b9a05580 Mon Sep 17 00:00:00 2001 From: Pk-web6936 <202365630+Pk-web6936@users.noreply.github.com> Date: Fri, 23 May 2025 12:28:03 +0330 Subject: [PATCH 52/81] Update kotlin version to 2.1.21 (#4583) * Update kotlin version to 2.1.21 * Update kotlin version to 2.1.21 --- README.md | 2 +- V2rayNG/gradle/libs.versions.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4f068ffb..4bd6f8ec 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ A V2Ray client for Android, support [Xray core](https://github.com/XTLS/Xray-core) and [v2fly core](https://github.com/v2fly/v2ray-core) [![API](https://img.shields.io/badge/API-21%2B-yellow.svg?style=flat)](https://developer.android.com/about/versions/lollipop) -[![Kotlin Version](https://img.shields.io/badge/Kotlin-2.1.20-blue.svg)](https://kotlinlang.org) +[![Kotlin Version](https://img.shields.io/badge/Kotlin-2.1.21-blue.svg)](https://kotlinlang.org) [![GitHub commit activity](https://img.shields.io/github/commit-activity/m/2dust/v2rayNG)](https://github.com/2dust/v2rayNG/commits/master) [![CodeFactor](https://www.codefactor.io/repository/github/2dust/v2rayng/badge)](https://www.codefactor.io/repository/github/2dust/v2rayng) [![GitHub Releases](https://img.shields.io/github/downloads/2dust/v2rayNG/latest/total?logo=github)](https://github.com/2dust/v2rayNG/releases) diff --git a/V2rayNG/gradle/libs.versions.toml b/V2rayNG/gradle/libs.versions.toml index 8c46149e..3a935aaa 100644 --- a/V2rayNG/gradle/libs.versions.toml +++ b/V2rayNG/gradle/libs.versions.toml @@ -2,7 +2,7 @@ agp = "8.9.3" desugarJdkLibs = "2.1.5" gradleLicensePlugin = "0.9.8" -kotlin = "2.1.20" +kotlin = "2.1.21" coreKtx = "1.16.0" junit = "4.13.2" junitVersion = "1.2.1" From d910b93525ec589349b6b387c06ce25ad2948a94 Mon Sep 17 00:00:00 2001 From: solokot Date: Sun, 25 May 2025 05:12:29 +0300 Subject: [PATCH 53/81] Update Russian translation (#4611) --- V2rayNG/app/src/main/res/values-ru/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/V2rayNG/app/src/main/res/values-ru/strings.xml b/V2rayNG/app/src/main/res/values-ru/strings.xml index 615a33d8..9b5e39f6 100644 --- a/V2rayNG/app/src/main/res/values-ru/strings.xml +++ b/V2rayNG/app/src/main/res/values-ru/strings.xml @@ -323,7 +323,7 @@ Найдена новая версия: %s Обновить Искать предварительный выпуск - Checking for update… + Проверка обновления… QR-код From 822c1de79c42854f555ca3de0e248036f16d72ae Mon Sep 17 00:00:00 2001 From: Hossein Abaspanah <63148255+hosseinabaspanah@users.noreply.github.com> Date: Sun, 25 May 2025 05:42:39 +0330 Subject: [PATCH 54/81] Update Luri Bakhtiari translation (#4610) --- V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 73a89fa3..4f129d9e 100644 --- a/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml +++ b/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml @@ -324,7 +324,7 @@ نوسخه نۊ ن جوست: %s سکو ورۊ رسۊوی کۊنین واجۊری نوسخه یل پؽش ز تیجنیڌن - Checking for update… + ورۊ رسۊوی ن هونی واجۊری اکونه... QRcode From 90ed02804cb03e2a61d5396ecc6dcf7bdbea9b0b Mon Sep 17 00:00:00 2001 From: Pk-web6936 <202365630+Pk-web6936@users.noreply.github.com> Date: Sun, 25 May 2025 05:42:45 +0330 Subject: [PATCH 55/81] Update Persian translate (#4607) --- V2rayNG/app/src/main/res/values-fa/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/V2rayNG/app/src/main/res/values-fa/strings.xml b/V2rayNG/app/src/main/res/values-fa/strings.xml index b5455fbb..16bbd4ff 100644 --- a/V2rayNG/app/src/main/res/values-fa/strings.xml +++ b/V2rayNG/app/src/main/res/values-fa/strings.xml @@ -321,7 +321,7 @@ نسخه جدید پیدا شد: %s اکنون به روز رسانی کنید بررسی نسخه پیش از انتشار - Checking for update… + در حال بررسی برای به‌روزرسانی… QRcode From 69c5bbfd3d7511e599836ea7716941f041f8b09a Mon Sep 17 00:00:00 2001 From: Hossein Abaspanah <63148255+hosseinabaspanah@users.noreply.github.com> Date: Sun, 25 May 2025 05:42:52 +0330 Subject: [PATCH 56/81] Improved Luri Bakhtiari Translation (#4600) --- V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 4f129d9e..41e81fbb 100644 --- a/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml +++ b/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml @@ -148,7 +148,7 @@ سامووا Mux ر وندن Mux - زل تر، ٱما گاشڌ منپیز زی قت بۊ بارت دؽوۉداری، TCP، UDP و QUIC ن ای لم سفارشی کۊنین. + زل تر، ٱما گاشڌ منپیز زی قت بۊ\nمخزن ترافیک TCP وا 8 منپیز پؽش فرز، بارت دؽوۉداری UDP وو QUIC ن ای لم سفارشی کۊنین. منپیزا TCP (تلایه منجا 1-1024) منپیزا XUDP (تلایه منجا 1-1024) دؽوۉداری QUIC من تۊنل mux From aa47fba20d583c9970fd03ee32f95ffe87b36bdc Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Sun, 25 May 2025 11:06:15 +0800 Subject: [PATCH 57/81] up 1.10.4 --- V2rayNG/app/build.gradle.kts | 4 ++-- V2rayNG/gradle/libs.versions.toml | 2 +- V2rayNG/gradle/wrapper/gradle-wrapper.properties | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/V2rayNG/app/build.gradle.kts b/V2rayNG/app/build.gradle.kts index f2c34017..c66250d9 100644 --- a/V2rayNG/app/build.gradle.kts +++ b/V2rayNG/app/build.gradle.kts @@ -12,8 +12,8 @@ android { applicationId = "com.v2ray.ang" minSdk = 21 targetSdk = 35 - versionCode = 653 - versionName = "1.10.3" + versionCode = 654 + versionName = "1.10.4" multiDexEnabled = true val abiFilterList = (properties["ABI_FILTERS"] as? String)?.split(';') diff --git a/V2rayNG/gradle/libs.versions.toml b/V2rayNG/gradle/libs.versions.toml index 3a935aaa..97e74cbf 100644 --- a/V2rayNG/gradle/libs.versions.toml +++ b/V2rayNG/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -agp = "8.9.3" +agp = "8.10.0" desugarJdkLibs = "2.1.5" gradleLicensePlugin = "0.9.8" kotlin = "2.1.21" diff --git a/V2rayNG/gradle/wrapper/gradle-wrapper.properties b/V2rayNG/gradle/wrapper/gradle-wrapper.properties index f221584f..b2eeb9db 100644 --- a/V2rayNG/gradle/wrapper/gradle-wrapper.properties +++ b/V2rayNG/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Thu Nov 14 12:42:51 BDT 2024 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From f305e26a395650301e5565d95ffb1d3199e846ed Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Sat, 31 May 2025 11:12:57 +0800 Subject: [PATCH 58/81] Fix the parsing problem of non-English domain https://github.com/2dust/v2rayNG/issues/4626 --- .../main/java/com/v2ray/ang/fmt/FmtBase.kt | 5 ++- .../main/java/com/v2ray/ang/fmt/HttpFmt.kt | 2 +- .../java/com/v2ray/ang/fmt/ShadowsocksFmt.kt | 2 +- .../main/java/com/v2ray/ang/fmt/SocksFmt.kt | 2 +- .../main/java/com/v2ray/ang/fmt/TrojanFmt.kt | 2 +- .../main/java/com/v2ray/ang/fmt/VlessFmt.kt | 2 +- .../main/java/com/v2ray/ang/fmt/VmessFmt.kt | 2 +- .../com/v2ray/ang/handler/AngConfigManager.kt | 2 +- .../main/java/com/v2ray/ang/util/HttpUtil.kt | 32 ++++++++++++++++--- .../test/java/com/v2ray/ang/HttpUtilTest.kt | 12 +++---- 10 files changed, 45 insertions(+), 18 deletions(-) diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt index cc9a70ff..873eaa6a 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt @@ -4,6 +4,7 @@ import com.v2ray.ang.AppConfig import com.v2ray.ang.dto.NetworkType import com.v2ray.ang.dto.ProfileItem import com.v2ray.ang.extension.isNotNullEmpty +import com.v2ray.ang.util.HttpUtil import com.v2ray.ang.util.Utils import java.net.URI @@ -149,6 +150,8 @@ open class FmtBase { return dicQuery } - + fun getServerAddress(profileItem: ProfileItem): String { + return HttpUtil.toIdnDomain(profileItem.server.orEmpty()) + } } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/HttpFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/HttpFmt.kt index 73def17e..8c641f24 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/HttpFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/HttpFmt.kt @@ -17,7 +17,7 @@ object HttpFmt : FmtBase() { val outboundBean = V2rayConfigManager.createInitOutbound(EConfigType.HTTP) outboundBean?.settings?.servers?.first()?.let { server -> - server.address = profileItem.server.orEmpty() + server.address = getServerAddress(profileItem) server.port = profileItem.serverPort.orEmpty().toInt() if (profileItem.username.isNotNullEmpty()) { val socksUsersBean = OutboundBean.OutSettingsBean.ServersBean.SocksUsersBean() diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/ShadowsocksFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/ShadowsocksFmt.kt index 172716c9..87ba74f8 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/ShadowsocksFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/ShadowsocksFmt.kt @@ -135,7 +135,7 @@ object ShadowsocksFmt : FmtBase() { val outboundBean = V2rayConfigManager.createInitOutbound(EConfigType.SHADOWSOCKS) outboundBean?.settings?.servers?.first()?.let { server -> - server.address = profileItem.server.orEmpty() + server.address = getServerAddress(profileItem) server.port = profileItem.serverPort.orEmpty().toInt() server.password = profileItem.password server.method = profileItem.method diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt index 6b727025..30bc08e4 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt @@ -64,7 +64,7 @@ object SocksFmt : FmtBase() { val outboundBean = V2rayConfigManager.createInitOutbound(EConfigType.SOCKS) outboundBean?.settings?.servers?.first()?.let { server -> - server.address = profileItem.server.orEmpty() + server.address = getServerAddress(profileItem) server.port = profileItem.serverPort.orEmpty().toInt() if (profileItem.username.isNotNullEmpty()) { val socksUsersBean = OutboundBean.OutSettingsBean.ServersBean.SocksUsersBean() diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt index 6ad0d71b..446ef99c 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt @@ -64,7 +64,7 @@ object TrojanFmt : FmtBase() { val outboundBean = V2rayConfigManager.createInitOutbound(EConfigType.TROJAN) outboundBean?.settings?.servers?.first()?.let { server -> - server.address = profileItem.server.orEmpty() + server.address = getServerAddress(profileItem) server.port = profileItem.serverPort.orEmpty().toInt() server.password = profileItem.password server.flow = profileItem.flow diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VlessFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VlessFmt.kt index f5cf9fff..9242f0ec 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VlessFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VlessFmt.kt @@ -60,7 +60,7 @@ object VlessFmt : FmtBase() { val outboundBean = V2rayConfigManager.createInitOutbound(EConfigType.VLESS) outboundBean?.settings?.vnext?.first()?.let { vnext -> - vnext.address = profileItem.server.orEmpty() + vnext.address = getServerAddress(profileItem) vnext.port = profileItem.serverPort.orEmpty().toInt() vnext.users[0].id = profileItem.password.orEmpty() vnext.users[0].encryption = profileItem.method diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt index c733f3dc..4201f4dc 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt @@ -172,7 +172,7 @@ object VmessFmt : FmtBase() { val outboundBean = V2rayConfigManager.createInitOutbound(EConfigType.VMESS) outboundBean?.settings?.vnext?.first()?.let { vnext -> - vnext.address = profileItem.server.orEmpty() + vnext.address = getServerAddress(profileItem) vnext.port = profileItem.serverPort.orEmpty().toInt() vnext.users[0].id = profileItem.password.orEmpty() vnext.users[0].security = profileItem.method 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 1dcd1276..d24ae0c2 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 @@ -415,7 +415,7 @@ object AngConfigManager { if (!it.second.enabled) { return 0 } - val url = HttpUtil.idnToASCII(it.second.url) + val url = HttpUtil.toIdnUrl(it.second.url) if (!Utils.isValidUrl(url)) { return 0 } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt index 9e08f663..7172728e 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt @@ -18,12 +18,14 @@ import java.net.URL object HttpUtil { /** - * Converts a URL string to its ASCII representation. + * Converts the domain part of a URL string to its IDN (Punycode, ASCII Compatible Encoding) format. * - * @param str The URL string to convert. - * @return The ASCII representation of the URL. + * For example, a URL like "https://例子.中国/path" will be converted to "https://xn--fsqu00a.xn--fiqs8s/path". + * + * @param str The URL string to convert (can contain non-ASCII characters in the domain). + * @return The URL string with the domain part converted to ASCII-compatible (Punycode) format. */ - fun idnToASCII(str: String): String { + fun toIdnUrl(str: String): String { val url = URL(str) val host = url.host val asciiHost = IDN.toASCII(url.host, IDN.ALLOW_UNASSIGNED) @@ -34,6 +36,28 @@ object HttpUtil { } } + /** + * Converts a Unicode domain name to its IDN (Punycode, ASCII Compatible Encoding) format. + * If the input is an IP address or already an ASCII domain, returns the original string. + * + * @param domain The domain string to convert (can include non-ASCII internationalized characters). + * @return The domain in ASCII-compatible (Punycode) format, or the original string if input is an IP or already ASCII. + */ + fun toIdnDomain(domain: String): String { + // Return as is if it's a pure IP address (IPv4 or IPv6) + if (Utils.isPureIpAddress(domain)) { + return domain + } + + // Return as is if already ASCII (English domain or already punycode) + if (domain.all { it.code < 128 }) { + return domain + } + + // Otherwise, convert to ASCII using IDN + return IDN.toASCII(domain, IDN.ALLOW_UNASSIGNED) + } + /** * Resolves a hostname to an IP address, returns original input if it's already an IP * diff --git a/V2rayNG/app/src/test/java/com/v2ray/ang/HttpUtilTest.kt b/V2rayNG/app/src/test/java/com/v2ray/ang/HttpUtilTest.kt index 207215e5..07d87f4d 100644 --- a/V2rayNG/app/src/test/java/com/v2ray/ang/HttpUtilTest.kt +++ b/V2rayNG/app/src/test/java/com/v2ray/ang/HttpUtilTest.kt @@ -10,31 +10,31 @@ class HttpUtilTest { fun testIdnToASCII() { // Regular URL remains unchanged val regularUrl = "https://example.com/path" - assertEquals(regularUrl, HttpUtil.idnToASCII(regularUrl)) + assertEquals(regularUrl, HttpUtil.toIdnUrl(regularUrl)) // Non-ASCII URL converts to ASCII (Punycode) val nonAsciiUrl = "https://例子.测试/path" val expectedNonAscii = "https://xn--fsqu00a.xn--0zwm56d/path" - assertEquals(expectedNonAscii, HttpUtil.idnToASCII(nonAsciiUrl)) + assertEquals(expectedNonAscii, HttpUtil.toIdnUrl(nonAsciiUrl)) // Mixed URL only converts the host part val mixedUrl = "https://例子.com/测试" val expectedMixed = "https://xn--fsqu00a.com/测试" - assertEquals(expectedMixed, HttpUtil.idnToASCII(mixedUrl)) + assertEquals(expectedMixed, HttpUtil.toIdnUrl(mixedUrl)) // URL with Basic Authentication using regular domain val basicAuthUrl = "https://user:password@example.com/path" - assertEquals(basicAuthUrl, HttpUtil.idnToASCII(basicAuthUrl)) + assertEquals(basicAuthUrl, HttpUtil.toIdnUrl(basicAuthUrl)) // URL with Basic Authentication using non-ASCII domain val basicAuthNonAscii = "https://user:password@例子.测试/path" val expectedBasicAuthNonAscii = "https://user:password@xn--fsqu00a.xn--0zwm56d/path" - assertEquals(expectedBasicAuthNonAscii, HttpUtil.idnToASCII(basicAuthNonAscii)) + assertEquals(expectedBasicAuthNonAscii, HttpUtil.toIdnUrl(basicAuthNonAscii)) // URL with non-ASCII username and password val nonAsciiAuth = "https://用户:密码@example.com/path" // Basic auth credentials should remain unchanged as they're percent-encoded separately - assertEquals(nonAsciiAuth, HttpUtil.idnToASCII(nonAsciiAuth)) + assertEquals(nonAsciiAuth, HttpUtil.toIdnUrl(nonAsciiAuth)) } From 9d1f98ff34c2e085389357c500e2ce22987cae9e Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Sat, 31 May 2025 14:03:52 +0800 Subject: [PATCH 59/81] Fix non-English domain https://github.com/2dust/v2rayNG/issues/4626 https://github.com/2dust/v2rayNG/commit/f305e26a395650301e5565d95ffb1d3199e846ed --- V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt index 873eaa6a..ec2d7801 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt @@ -27,7 +27,7 @@ open class FmtBase { val url = String.format( "%s@%s:%s", Utils.urlEncode(userInfo ?: ""), - Utils.getIpv6Address(config.server), + Utils.getIpv6Address(HttpUtil.toIdnDomain(config.server.orEmpty())), config.serverPort ) From 3ead542e2b64851caa6893af004fb3bf5d351be6 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Sat, 7 Jun 2025 11:20:37 +0800 Subject: [PATCH 60/81] VPN bypass LAN By default --- .../app/src/main/java/com/v2ray/ang/handler/SettingsManager.kt | 2 +- V2rayNG/app/src/main/res/xml/pref_settings.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SettingsManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SettingsManager.kt index c528aeb2..a488e593 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SettingsManager.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SettingsManager.kt @@ -159,7 +159,7 @@ object SettingsManager { * @return True if bypassing LAN, false otherwise. */ fun routingRulesetsBypassLan(): Boolean { - val vpnBypassLan = MmkvManager.decodeSettingsString(AppConfig.PREF_VPN_BYPASS_LAN) ?: "0" + val vpnBypassLan = MmkvManager.decodeSettingsString(AppConfig.PREF_VPN_BYPASS_LAN) ?: "1" if (vpnBypassLan == "1") { return true } else if (vpnBypassLan == "2") { diff --git a/V2rayNG/app/src/main/res/xml/pref_settings.xml b/V2rayNG/app/src/main/res/xml/pref_settings.xml index 75cad848..cbfbb932 100644 --- a/V2rayNG/app/src/main/res/xml/pref_settings.xml +++ b/V2rayNG/app/src/main/res/xml/pref_settings.xml @@ -56,7 +56,7 @@ android:title="@string/title_pref_vpn_dns" /> Date: Sat, 7 Jun 2025 11:20:41 +0800 Subject: [PATCH 61/81] Update libs.versions.toml --- V2rayNG/gradle/libs.versions.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/V2rayNG/gradle/libs.versions.toml b/V2rayNG/gradle/libs.versions.toml index 97e74cbf..5bdd1236 100644 --- a/V2rayNG/gradle/libs.versions.toml +++ b/V2rayNG/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -agp = "8.10.0" +agp = "8.10.1" desugarJdkLibs = "2.1.5" gradleLicensePlugin = "0.9.8" kotlin = "2.1.21" @@ -7,7 +7,7 @@ coreKtx = "1.16.0" junit = "4.13.2" junitVersion = "1.2.1" espressoCore = "3.6.1" -appcompat = "1.7.0" +appcompat = "1.7.1" material = "1.12.0" activity = "1.10.1" constraintlayout = "2.2.1" @@ -21,7 +21,7 @@ toasty = "1.5.2" editorkit = "2.9.0" core = "3.5.3" workRuntimeKtx = "2.10.1" -lifecycleViewmodelKtx = "2.9.0" +lifecycleViewmodelKtx = "2.9.1" multidex = "2.0.1" mockitoMockitoInline = "5.2.0" flexbox = "3.0.0" From ea088376ac78ee81cd8552c1da71da78b33016fd Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Sun, 8 Jun 2025 09:25:46 +0800 Subject: [PATCH 62/81] Update AndroidLibXrayLite --- AndroidLibXrayLite | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AndroidLibXrayLite b/AndroidLibXrayLite index ddcaecad..a031a553 160000 --- a/AndroidLibXrayLite +++ b/AndroidLibXrayLite @@ -1 +1 @@ -Subproject commit ddcaecad0ae2f0816b33b8a56d1e36fce4efcae4 +Subproject commit a031a553f6feb77b3b9db060435cf0c2bff043e6 From fdb67a86f46b4b53b5726aa5986e3f9ba3372189 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Sun, 8 Jun 2025 09:26:36 +0800 Subject: [PATCH 63/81] up 1.10.5 --- V2rayNG/app/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/V2rayNG/app/build.gradle.kts b/V2rayNG/app/build.gradle.kts index c66250d9..eed45b22 100644 --- a/V2rayNG/app/build.gradle.kts +++ b/V2rayNG/app/build.gradle.kts @@ -12,8 +12,8 @@ android { applicationId = "com.v2ray.ang" minSdk = 21 targetSdk = 35 - versionCode = 654 - versionName = "1.10.4" + versionCode = 655 + versionName = "1.10.5" multiDexEnabled = true val abiFilterList = (properties["ABI_FILTERS"] as? String)?.split(';') From fff6ab30e6000d95885706af34d3e35b8d2aeb60 Mon Sep 17 00:00:00 2001 From: patterniha <71074308+patterniha@users.noreply.github.com> Date: Sat, 14 Jun 2025 09:29:59 +0330 Subject: [PATCH 64/81] Xray-core default FakeIPv6 Pool should not bypass and should route (#4649) * Update V2RayVpnService.kt * Update V2RayVpnService.kt * Update AppConfig.kt --- V2rayNG/app/src/main/java/com/v2ray/ang/AppConfig.kt | 2 +- .../app/src/main/java/com/v2ray/ang/service/V2RayVpnService.kt | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) 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 9e1b7918..4099e7f9 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/AppConfig.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/AppConfig.kt @@ -189,7 +189,7 @@ object AppConfig { val DNS_YANDEX_ADDRESSES = arrayListOf("77.88.8.8", "77.88.8.1", "2a02:6b8::feed:0ff", "2a02:6b8:0:1::feed:0ff") //minimum list https://serverfault.com/a/304791 - val BYPASS_PRIVATE_IP_LIST = arrayListOf( + val ROUTED_IP_LIST = arrayListOf( "0.0.0.0/5", "8.0.0.0/7", "11.0.0.0/8", diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayVpnService.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayVpnService.kt index 9fc24d56..6d30843c 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayVpnService.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayVpnService.kt @@ -167,7 +167,7 @@ class V2RayVpnService : VpnService(), ServiceControl { //builder.addDnsServer(PRIVATE_VLAN4_ROUTER) val bypassLan = SettingsManager.routingRulesetsBypassLan() if (bypassLan) { - AppConfig.BYPASS_PRIVATE_IP_LIST.forEach { + AppConfig.ROUTED_IP_LIST.forEach { val addr = it.split('/') builder.addRoute(addr[0], addr[1].toInt()) } @@ -179,6 +179,7 @@ class V2RayVpnService : VpnService(), ServiceControl { builder.addAddress(PRIVATE_VLAN6_CLIENT, 126) if (bypassLan) { builder.addRoute("2000::", 3) //currently only 1/8 of total ipV6 is in use + builder.addRoute("fc00::", 18) //Xray-core default FakeIPv6 Pool } else { builder.addRoute("::", 0) } From 69e27ed3bb872efc14e98d20c3f4c29a139d055f Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Sat, 14 Jun 2025 14:26:33 +0800 Subject: [PATCH 65/81] Fix log for plugin --- V2rayNG/app/src/main/java/com/v2ray/ang/util/PluginUtil.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/util/PluginUtil.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/util/PluginUtil.kt index 48e04b6d..2b9f71aa 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/util/PluginUtil.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/util/PluginUtil.kt @@ -28,13 +28,17 @@ object PluginUtil { fun runPlugin(context: Context, config: ProfileItem?, socksPort: Int?) { Log.i(AppConfig.TAG, "Starting plugin execution") - if (config == null || socksPort == null) { + if (config == null) { Log.w(AppConfig.TAG, "Cannot run plugin: config is null") return } try { if (config.configType == EConfigType.HYSTERIA2) { + if (socksPort == null) { + Log.w(AppConfig.TAG, "Cannot run plugin: socksPort is null") + return + } Log.i(AppConfig.TAG, "Running Hysteria2 plugin") val configFile = genConfigHy2(context, config, socksPort) ?: return val cmd = genCmdHy2(context, configFile) From 6f0b3ce99028a3ed40ae4c8e1ec57792def99cf4 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Sat, 14 Jun 2025 14:26:37 +0800 Subject: [PATCH 66/81] Update AndroidLibXrayLite --- AndroidLibXrayLite | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AndroidLibXrayLite b/AndroidLibXrayLite index a031a553..4de15a6c 160000 --- a/AndroidLibXrayLite +++ b/AndroidLibXrayLite @@ -1 +1 @@ -Subproject commit a031a553f6feb77b3b9db060435cf0c2bff043e6 +Subproject commit 4de15a6c8d2c4d34b0d3cdce61955f8f5b113207 From 51eabe5440ed6704c596b51a049bfa7d51aa227e Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Sat, 14 Jun 2025 14:27:09 +0800 Subject: [PATCH 67/81] up 1.10.6 --- V2rayNG/app/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/V2rayNG/app/build.gradle.kts b/V2rayNG/app/build.gradle.kts index eed45b22..82bfe062 100644 --- a/V2rayNG/app/build.gradle.kts +++ b/V2rayNG/app/build.gradle.kts @@ -12,8 +12,8 @@ android { applicationId = "com.v2ray.ang" minSdk = 21 targetSdk = 35 - versionCode = 655 - versionName = "1.10.5" + versionCode = 656 + versionName = "1.10.6" multiDexEnabled = true val abiFilterList = (properties["ABI_FILTERS"] as? String)?.split(';') From 72194252583b863aa17d4079704c9fb779167b2e Mon Sep 17 00:00:00 2001 From: DHR60 Date: Sun, 15 Jun 2025 09:46:30 +0800 Subject: [PATCH 68/81] Cloudflare DNS Hosts (#4661) --- .../src/main/java/com/v2ray/ang/AppConfig.kt | 8 +++- .../v2ray/ang/handler/V2rayConfigManager.kt | 38 +++++++++---------- 2 files changed, 25 insertions(+), 21 deletions(-) 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 4099e7f9..3047874e 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/AppConfig.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/AppConfig.kt @@ -168,7 +168,9 @@ object AppConfig { // Android Private DNS constants const val DNS_DNSPOD_DOMAIN = "dot.pub" const val DNS_ALIDNS_DOMAIN = "dns.alidns.com" - const val DNS_CLOUDFLARE_DOMAIN = "one.one.one.one" + const val DNS_CLOUDFLARE_ONE_DOMAIN = "one.one.one.one" + const val DNS_CLOUDFLARE_DNS_COM_DOMAIN = "dns.cloudflare.com" + const val DNS_CLOUDFLARE_DNS_DOMAIN = "cloudflare-dns.com" const val DNS_GOOGLE_DOMAIN = "dns.google" const val DNS_QUAD9_DOMAIN = "dns.quad9.net" const val DNS_YANDEX_DOMAIN = "common.dot.dns.yandex.net" @@ -182,7 +184,9 @@ object AppConfig { const val HEADER_TYPE_HTTP = "http" val DNS_ALIDNS_ADDRESSES = arrayListOf("223.5.5.5", "223.6.6.6", "2400:3200::1", "2400:3200:baba::1") - val DNS_CLOUDFLARE_ADDRESSES = arrayListOf("1.1.1.1", "1.0.0.1", "2606:4700:4700::1111", "2606:4700:4700::1001") + val DNS_CLOUDFLARE_ONE_ADDRESSES = arrayListOf("1.1.1.1", "1.0.0.1", "2606:4700:4700::1111", "2606:4700:4700::1001") + val DNS_CLOUDFLARE_DNS_COM_ADDRESSES = arrayListOf("104.16.132.229", "104.16.133.229", "2606:4700::6810:84e5", "2606:4700::6810:85e5") + val DNS_CLOUDFLARE_DNS_ADDRESSES = arrayListOf("104.16.248.249", "104.16.249.249", "2606:4700::6810:f8f9", "2606:4700::6810:f9f9") val DNS_DNSPOD_ADDRESSES = arrayListOf("1.12.12.12", "120.53.53.53") val DNS_GOOGLE_ADDRESSES = arrayListOf("8.8.8.8", "8.8.4.4", "2001:4860:4860::8888", "2001:4860:4860::8844") val DNS_QUAD9_ADDRESSES = arrayListOf("9.9.9.9", "149.112.112.112", "2620:fe::fe", "2620:fe::9") 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 8ef7a7b7..c0e6fae8 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 @@ -16,7 +16,6 @@ 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.HttpFmt -import com.v2ray.ang.fmt.Hysteria2Fmt import com.v2ray.ang.fmt.ShadowsocksFmt import com.v2ray.ang.fmt.SocksFmt import com.v2ray.ang.fmt.TrojanFmt @@ -479,6 +478,25 @@ object V2rayConfigManager { ) } + //block dns + val blkDomain = getUserRule2Domain(AppConfig.TAG_BLOCKED) + if (blkDomain.isNotEmpty()) { + hosts.putAll(blkDomain.map { it to AppConfig.LOOPBACK }) + } + + // hardcode googleapi rule to fix play store problems + hosts[AppConfig.GOOGLEAPIS_CN_DOMAIN] = AppConfig.GOOGLEAPIS_COM_DOMAIN + + // hardcode popular Android Private DNS rule to fix localhost DNS problem + hosts[AppConfig.DNS_ALIDNS_DOMAIN] = AppConfig.DNS_ALIDNS_ADDRESSES + hosts[AppConfig.DNS_CLOUDFLARE_ONE_DOMAIN] = AppConfig.DNS_CLOUDFLARE_ONE_ADDRESSES + hosts[AppConfig.DNS_CLOUDFLARE_DNS_COM_DOMAIN] = AppConfig.DNS_CLOUDFLARE_DNS_COM_ADDRESSES + hosts[AppConfig.DNS_CLOUDFLARE_DNS_DOMAIN] = AppConfig.DNS_CLOUDFLARE_DNS_ADDRESSES + hosts[AppConfig.DNS_DNSPOD_DOMAIN] = AppConfig.DNS_DNSPOD_ADDRESSES + hosts[AppConfig.DNS_GOOGLE_DOMAIN] = AppConfig.DNS_GOOGLE_ADDRESSES + hosts[AppConfig.DNS_QUAD9_DOMAIN] = AppConfig.DNS_QUAD9_ADDRESSES + hosts[AppConfig.DNS_YANDEX_DOMAIN] = AppConfig.DNS_YANDEX_ADDRESSES + //User DNS hosts try { val userHosts = MmkvManager.decodeSettingsString(AppConfig.PREF_DNS_HOSTS) @@ -493,24 +511,6 @@ object V2rayConfigManager { Log.e(AppConfig.TAG, "Failed to configure user DNS hosts", e) } - //block dns - val blkDomain = getUserRule2Domain(AppConfig.TAG_BLOCKED) - if (blkDomain.isNotEmpty()) { - hosts.putAll(blkDomain.map { it to AppConfig.LOOPBACK }) - } - - // hardcode googleapi rule to fix play store problems - hosts[AppConfig.GOOGLEAPIS_CN_DOMAIN] = AppConfig.GOOGLEAPIS_COM_DOMAIN - - // hardcode popular Android Private DNS rule to fix localhost DNS problem - hosts[AppConfig.DNS_ALIDNS_DOMAIN] = AppConfig.DNS_ALIDNS_ADDRESSES - hosts[AppConfig.DNS_CLOUDFLARE_DOMAIN] = AppConfig.DNS_CLOUDFLARE_ADDRESSES - hosts[AppConfig.DNS_DNSPOD_DOMAIN] = AppConfig.DNS_DNSPOD_ADDRESSES - hosts[AppConfig.DNS_GOOGLE_DOMAIN] = AppConfig.DNS_GOOGLE_ADDRESSES - hosts[AppConfig.DNS_QUAD9_DOMAIN] = AppConfig.DNS_QUAD9_ADDRESSES - hosts[AppConfig.DNS_YANDEX_DOMAIN] = AppConfig.DNS_YANDEX_ADDRESSES - - // DNS dns v2rayConfig.dns = V2rayConfig.DnsBean( servers = servers, From e0881caab4312beebd48bd90b4771f11df2bfe98 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Tue, 17 Jun 2025 13:43:03 +0800 Subject: [PATCH 69/81] Fix missing sockopt.domainStrategy (#4673) * Fix missing sockopt.domainStrategy * Fix --- .../main/java/com/v2ray/ang/handler/V2rayConfigManager.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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 c0e6fae8..ddbc78a3 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 @@ -828,7 +828,11 @@ object V2rayConfigManager { for (item in proxyOutboundList) { val domain = item.getServerAddress() if (domain.isNullOrEmpty()) continue - if (newHosts.containsKey(domain)) continue + + if (newHosts.containsKey(domain)) { + item.ensureSockopt().domainStrategy = if (preferIpv6) "UseIPv6v4" else "UseIPv4v6" + continue + } val resolvedIps = HttpUtil.resolveHostToIP(domain, preferIpv6) if (resolvedIps.isNullOrEmpty()) continue From 1a5e105212607fd4bc86421b152b5969548ec81c Mon Sep 17 00:00:00 2001 From: Ural Khamitov Date: Wed, 18 Jun 2025 13:17:28 +0500 Subject: [PATCH 70/81] Fix blinking QSTile when QS panel is opening (#4676) --- .../src/main/java/com/v2ray/ang/service/QSTileService.kt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/QSTileService.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/QSTileService.kt index db12287d..7aecf634 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/QSTileService.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/QSTileService.kt @@ -25,14 +25,13 @@ class QSTileService : TileService() { * @param state The state to set. */ fun setState(state: Int) { + qsTile?.icon = Icon.createWithResource(applicationContext, R.drawable.ic_stat_name) if (state == Tile.STATE_INACTIVE) { qsTile?.state = Tile.STATE_INACTIVE qsTile?.label = getString(R.string.app_name) - qsTile?.icon = Icon.createWithResource(applicationContext, R.drawable.ic_stat_name) } else if (state == Tile.STATE_ACTIVE) { qsTile?.state = Tile.STATE_ACTIVE qsTile?.label = V2RayServiceManager.getRunningServerName() - qsTile?.icon = Icon.createWithResource(applicationContext, R.drawable.ic_stat_name) } qsTile?.updateTile() @@ -45,7 +44,11 @@ class QSTileService : TileService() { override fun onStartListening() { super.onStartListening() - setState(Tile.STATE_INACTIVE) + if (V2RayServiceManager.isRunning()) { + setState(Tile.STATE_ACTIVE) + } else { + setState(Tile.STATE_INACTIVE) + } mMsgReceive = ReceiveMessageHandler(this) val mFilter = IntentFilter(AppConfig.BROADCAST_ACTION_ACTIVITY) ContextCompat.registerReceiver(applicationContext, mMsgReceive, mFilter, Utils.receiverFlags()) From e077c181086dbdbcd8bddffaf2f6fb6141f1bfb4 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Thu, 19 Jun 2025 14:40:07 +0800 Subject: [PATCH 71/81] Improved update checking and prompts in case of abnormality --- .../v2ray/ang/handler/UpdateCheckerManager.kt | 5 ----- .../com/v2ray/ang/ui/CheckUpdateActivity.kt | 17 ++++++++++++----- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/UpdateCheckerManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/UpdateCheckerManager.kt index e152002f..37b55c2e 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/UpdateCheckerManager.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/UpdateCheckerManager.kt @@ -17,7 +17,6 @@ import java.io.FileOutputStream object UpdateCheckerManager { suspend fun checkForUpdate(includePreRelease: Boolean = false): CheckUpdateResult = withContext(Dispatchers.IO) { - try { val url = if (includePreRelease) { AppConfig.APP_API_URL } else { @@ -53,10 +52,6 @@ object UpdateCheckerManager { } else { CheckUpdateResult(hasUpdate = false) } - } catch (e: Exception) { - Log.e(AppConfig.TAG, "Failed to check for updates: ${e.message}") - return@withContext CheckUpdateResult(hasUpdate = false, error = e.message) - } } suspend fun downloadApk(context: Context, downloadUrl: String): File? = withContext(Dispatchers.IO) { diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/CheckUpdateActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/CheckUpdateActivity.kt index 8f464a49..a9b698c5 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/CheckUpdateActivity.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/CheckUpdateActivity.kt @@ -1,6 +1,7 @@ package com.v2ray.ang.ui import android.os.Bundle +import android.util.Log import androidx.appcompat.app.AlertDialog import androidx.lifecycle.lifecycleScope import com.v2ray.ang.AppConfig @@ -9,6 +10,7 @@ import com.v2ray.ang.R import com.v2ray.ang.databinding.ActivityCheckUpdateBinding import com.v2ray.ang.dto.CheckUpdateResult import com.v2ray.ang.extension.toast +import com.v2ray.ang.extension.toastError import com.v2ray.ang.extension.toastSuccess import com.v2ray.ang.handler.MmkvManager import com.v2ray.ang.handler.SpeedtestManager @@ -46,11 +48,16 @@ class CheckUpdateActivity : BaseActivity() { toast(R.string.update_checking_for_update) lifecycleScope.launch { - val result = UpdateCheckerManager.checkForUpdate(includePreRelease) - if (result.hasUpdate) { - showUpdateDialog(result) - } else { - toastSuccess(R.string.update_already_latest_version) + try { + val result = UpdateCheckerManager.checkForUpdate(includePreRelease) + if (result.hasUpdate) { + showUpdateDialog(result) + } else { + toastSuccess(R.string.update_already_latest_version) + } + } catch (e: Exception) { + Log.e(AppConfig.TAG, "Failed to check for updates: ${e.message}") + toastError(e.message ?: getString(R.string.toast_failure)) } } } From f68c35371527c56a19a388e59e6ef237b956a3e8 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Thu, 19 Jun 2025 14:40:11 +0800 Subject: [PATCH 72/81] Update AndroidLibXrayLite --- AndroidLibXrayLite | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AndroidLibXrayLite b/AndroidLibXrayLite index 4de15a6c..8ad3e1dd 160000 --- a/AndroidLibXrayLite +++ b/AndroidLibXrayLite @@ -1 +1 @@ -Subproject commit 4de15a6c8d2c4d34b0d3cdce61955f8f5b113207 +Subproject commit 8ad3e1ddf165d8d67e488346b2faa9153d3e33a4 From 94cc72d2b991ca6a66d0b6c70af63c9f88ec4fe1 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Thu, 19 Jun 2025 14:40:47 +0800 Subject: [PATCH 73/81] up 1.10.7 --- V2rayNG/app/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/V2rayNG/app/build.gradle.kts b/V2rayNG/app/build.gradle.kts index 82bfe062..2e3ab596 100644 --- a/V2rayNG/app/build.gradle.kts +++ b/V2rayNG/app/build.gradle.kts @@ -12,8 +12,8 @@ android { applicationId = "com.v2ray.ang" minSdk = 21 targetSdk = 35 - versionCode = 656 - versionName = "1.10.6" + versionCode = 657 + versionName = "1.10.7" multiDexEnabled = true val abiFilterList = (properties["ABI_FILTERS"] as? String)?.split(';') From 2fb6e62e1309d29437c1f4147f5531056b045dab Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Fri, 27 Jun 2025 16:09:03 +0800 Subject: [PATCH 74/81] Added setting option for VPN interface address https://github.com/2dust/v2rayNG/issues/4641 --- .../src/main/java/com/v2ray/ang/AppConfig.kt | 1 + .../ang/dto/VpnInterfaceAddressConfig.kt | 39 +++++++++++++++++++ .../com/v2ray/ang/handler/SettingsManager.kt | 14 +++++++ .../com/v2ray/ang/service/V2RayVpnService.kt | 15 +++---- .../java/com/v2ray/ang/ui/SettingsActivity.kt | 4 +- .../v2ray/ang/viewmodel/SettingsViewModel.kt | 1 + .../app/src/main/res/values-ar/strings.xml | 2 + .../app/src/main/res/values-bn/strings.xml | 2 + .../src/main/res/values-bqi-rIR/strings.xml | 2 + .../app/src/main/res/values-fa/strings.xml | 2 + .../app/src/main/res/values-ru/strings.xml | 2 + .../app/src/main/res/values-vi/strings.xml | 2 + .../src/main/res/values-zh-rCN/strings.xml | 2 + .../src/main/res/values-zh-rTW/strings.xml | 3 ++ V2rayNG/app/src/main/res/values/arrays.xml | 20 ++++++++++ V2rayNG/app/src/main/res/values/strings.xml | 2 + .../app/src/main/res/xml/pref_settings.xml | 14 +++++-- 17 files changed, 114 insertions(+), 13 deletions(-) create mode 100644 V2rayNG/app/src/main/java/com/v2ray/ang/dto/VpnInterfaceAddressConfig.kt 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 3047874e..a5d6dbdc 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/AppConfig.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/AppConfig.kt @@ -26,6 +26,7 @@ object AppConfig { const val PREF_LOCAL_DNS_PORT = "pref_local_dns_port" const val PREF_VPN_DNS = "pref_vpn_dns" const val PREF_VPN_BYPASS_LAN = "pref_vpn_bypass_lan" + const val PREF_VPN_INTERFACE_ADDRESS_CONFIG_INDEX = "pref_vpn_interface_address_config_index" const val PREF_ROUTING_DOMAIN_STRATEGY = "pref_routing_domain_strategy" const val PREF_ROUTING_RULESET = "pref_routing_ruleset" const val PREF_MUX_ENABLED = "pref_mux_enabled" diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/VpnInterfaceAddressConfig.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/VpnInterfaceAddressConfig.kt new file mode 100644 index 00000000..6b7bc379 --- /dev/null +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/VpnInterfaceAddressConfig.kt @@ -0,0 +1,39 @@ +package com.v2ray.ang.dto + +/** + * VPN interface address configuration enum class + * Defines predefined IPv4 and IPv6 address pairs for VPN TUN interface configuration. + * Each option provides client and router addresses to establish point-to-point VPN tunnels. + */ +enum class VpnInterfaceAddressConfig( + val displayName: String, + val ipv4Client: String, + val ipv4Router: String, + val ipv6Client: String, + val ipv6Router: String +) { + OPTION_1("10.10.14.x", "10.10.14.1", "10.10.14.2", "fc00::10:10:14:1", "fc00::10:10:14:2"), + OPTION_2("10.1.0.x", "10.1.0.1", "10.1.0.2", "fc00::10:1:0:1", "fc00::10:1:0:2"), + OPTION_3("10.0.0.x", "10.0.0.1", "10.0.0.2", "fc00::10:0:0:1", "fc00::10:0:0:2"), + OPTION_4("172.31.0.x", "172.31.0.1", "172.31.0.2", "fc00::172:31:0:1", "fc00::172:31:0:2"), + OPTION_5("172.20.0.x", "172.20.0.1", "172.20.0.2", "fc00::172:20:0:1", "fc00::172:20:0:2"), + OPTION_6("172.16.0.x", "172.16.0.1", "172.16.0.2", "fc00::172:16:0:1", "fc00::172:16:0:2"), + OPTION_7("192.168.100.x", "192.168.100.1", "192.168.100.2", "fc00::192:168:100:1", "fc00::192:168:100:2"); + + companion object { + /** + * Retrieves the VPN interface address configuration based on the specified index. + * + * @param index The configuration index (0-based) corresponding to user selection + * @return The VpnInterfaceAddressConfig instance at the specified index, + * or OPTION_1 (default) if the index is out of bounds + */ + fun getConfigByIndex(index: Int): VpnInterfaceAddressConfig { + return if (index in values().indices) { + values()[index] + } else { + OPTION_1 // Default to the first configuration + } + } + } +} diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SettingsManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SettingsManager.kt index a488e593..b2e23f7f 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SettingsManager.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SettingsManager.kt @@ -16,6 +16,7 @@ import com.v2ray.ang.dto.ProfileItem import com.v2ray.ang.dto.RoutingType import com.v2ray.ang.dto.RulesetItem import com.v2ray.ang.dto.V2rayConfig +import com.v2ray.ang.dto.VpnInterfaceAddressConfig import com.v2ray.ang.handler.MmkvManager.decodeServerConfig import com.v2ray.ang.handler.MmkvManager.decodeServerList import com.v2ray.ang.util.JsonUtil @@ -356,4 +357,17 @@ object SettingsManager { "2" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) } } + + /** + * Retrieves the currently selected VPN interface address configuration. + * This method reads the user's preference for VPN interface addressing and returns + * the corresponding configuration containing IPv4 and IPv6 addresses. + * + * @return The selected VpnInterfaceAddressConfig instance, or the default configuration + * if no valid selection is found or if the stored index is invalid. + */ + fun getCurrentVpnInterfaceAddressConfig(): VpnInterfaceAddressConfig { + val selectedIndex = MmkvManager.decodeSettingsString(AppConfig.PREF_VPN_INTERFACE_ADDRESS_CONFIG_INDEX, "0")?.toInt() + return VpnInterfaceAddressConfig.getConfigByIndex(selectedIndex ?: 0) + } } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayVpnService.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayVpnService.kt index 6d30843c..d734c299 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayVpnService.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayVpnService.kt @@ -33,12 +33,7 @@ import java.lang.ref.SoftReference class V2RayVpnService : VpnService(), ServiceControl { companion object { private const val VPN_MTU = 1500 - private const val PRIVATE_VLAN4_CLIENT = "10.10.14.1" - private const val PRIVATE_VLAN4_ROUTER = "10.10.14.2" - private const val PRIVATE_VLAN6_CLIENT = "fc00::10:10:14:1" - private const val PRIVATE_VLAN6_ROUTER = "fc00::10:10:14:2" private const val TUN2SOCKS = "libtun2socks.so" - } private lateinit var mInterface: ParcelFileDescriptor @@ -160,10 +155,11 @@ class V2RayVpnService : VpnService(), ServiceControl { // If the old interface has exactly the same parameters, use it! // Configure a builder while parsing the parameters. val builder = Builder() + val vpnConfig = SettingsManager.getCurrentVpnInterfaceAddressConfig() //val enableLocalDns = defaultDPreference.getPrefBoolean(AppConfig.PREF_LOCAL_DNS_ENABLED, false) builder.setMtu(VPN_MTU) - builder.addAddress(PRIVATE_VLAN4_CLIENT, 30) + builder.addAddress(vpnConfig.ipv4Client, 30) //builder.addDnsServer(PRIVATE_VLAN4_ROUTER) val bypassLan = SettingsManager.routingRulesetsBypassLan() if (bypassLan) { @@ -176,7 +172,7 @@ class V2RayVpnService : VpnService(), ServiceControl { } if (MmkvManager.decodeSettingsBool(AppConfig.PREF_PREFER_IPV6) == true) { - builder.addAddress(PRIVATE_VLAN6_CLIENT, 126) + builder.addAddress(vpnConfig.ipv6Client, 126) if (bypassLan) { builder.addRoute("2000::", 3) //currently only 1/8 of total ipV6 is in use builder.addRoute("fc00::", 18) //Xray-core default FakeIPv6 Pool @@ -260,9 +256,10 @@ class V2RayVpnService : VpnService(), ServiceControl { private fun runTun2socks() { Log.i(AppConfig.TAG, "Start run $TUN2SOCKS") val socksPort = SettingsManager.getSocksPort() + val vpnConfig = SettingsManager.getCurrentVpnInterfaceAddressConfig() val cmd = arrayListOf( File(applicationContext.applicationInfo.nativeLibraryDir, TUN2SOCKS).absolutePath, - "--netif-ipaddr", PRIVATE_VLAN4_ROUTER, + "--netif-ipaddr", vpnConfig.ipv4Router, "--netif-netmask", "255.255.255.252", "--socks-server-addr", "$LOOPBACK:${socksPort}", "--tunmtu", VPN_MTU.toString(), @@ -273,7 +270,7 @@ class V2RayVpnService : VpnService(), ServiceControl { if (MmkvManager.decodeSettingsBool(AppConfig.PREF_PREFER_IPV6)) { cmd.add("--netif-ip6addr") - cmd.add(PRIVATE_VLAN6_ROUTER) + cmd.add(vpnConfig.ipv6Router) } if (MmkvManager.decodeSettingsBool(AppConfig.PREF_LOCAL_DNS_ENABLED)) { val localDnsPort = Utils.parseInt(MmkvManager.decodeSettingsString(AppConfig.PREF_LOCAL_DNS_PORT), AppConfig.PORT_LOCAL_DNS.toInt()) 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 4ec2294a..515bc65f 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 @@ -44,6 +44,7 @@ class SettingsActivity : BaseActivity() { private val localDnsPort by lazy { findPreference(AppConfig.PREF_LOCAL_DNS_PORT) } private val vpnDns by lazy { findPreference(AppConfig.PREF_VPN_DNS) } private val vpnBypassLan by lazy { findPreference(AppConfig.PREF_VPN_BYPASS_LAN) } + private val vpnInterfaceAddress by lazy { findPreference(AppConfig.PREF_VPN_INTERFACE_ADDRESS_CONFIG_INDEX) } private val mux by lazy { findPreference(AppConfig.PREF_MUX_ENABLED) } private val muxConcurrency by lazy { findPreference(AppConfig.PREF_MUX_CONCURRENCY) } @@ -249,6 +250,7 @@ class SettingsActivity : BaseActivity() { listOf( AppConfig.PREF_VPN_BYPASS_LAN, + AppConfig.PREF_VPN_INTERFACE_ADDRESS_CONFIG_INDEX, AppConfig.PREF_ROUTING_DOMAIN_STRATEGY, AppConfig.PREF_MUX_XUDP_QUIC, AppConfig.PREF_FRAGMENT_PACKETS, @@ -273,7 +275,7 @@ class SettingsActivity : BaseActivity() { localDnsPort?.isEnabled = vpn vpnDns?.isEnabled = vpn vpnBypassLan?.isEnabled = vpn - vpn + vpnInterfaceAddress?.isEnabled = vpn if (vpn) { updateLocalDns( MmkvManager.decodeSettingsBool( 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 d78b1307..e12a56e5 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 @@ -41,6 +41,7 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application AppConfig.PREF_MODE, AppConfig.PREF_VPN_DNS, AppConfig.PREF_VPN_BYPASS_LAN, + AppConfig.PREF_VPN_INTERFACE_ADDRESS_CONFIG_INDEX, AppConfig.PREF_REMOTE_DNS, AppConfig.PREF_DOMESTIC_DNS, AppConfig.PREF_DNS_HOSTS, diff --git a/V2rayNG/app/src/main/res/values-ar/strings.xml b/V2rayNG/app/src/main/res/values-ar/strings.xml index 49b724b1..b7843b69 100644 --- a/V2rayNG/app/src/main/res/values-ar/strings.xml +++ b/V2rayNG/app/src/main/res/values-ar/strings.xml @@ -181,6 +181,8 @@ VPN DNS (IPv4/v6 فقط) Does VPN bypass LAN + VPN Interface Address + DNS المحلي (اختياري) DNS diff --git a/V2rayNG/app/src/main/res/values-bn/strings.xml b/V2rayNG/app/src/main/res/values-bn/strings.xml index bd7c492f..888081e5 100644 --- a/V2rayNG/app/src/main/res/values-bn/strings.xml +++ b/V2rayNG/app/src/main/res/values-bn/strings.xml @@ -181,6 +181,8 @@ VPN DNS (শুধুমাত্র IPv4/v6) Does VPN bypass LAN + VPN Interface Address + ঘরোয়া DNS (ঐচ্ছিক) DNS 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 41e81fbb..fe0738f1 100644 --- a/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml +++ b/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml @@ -181,6 +181,8 @@ VPN DNS (تینا IPv4/v6) VPN ز شبکه مهلی اگوڌرته؟ + VPN Interface Address + DNS منی (اختیاری) DNS diff --git a/V2rayNG/app/src/main/res/values-fa/strings.xml b/V2rayNG/app/src/main/res/values-fa/strings.xml index 16bbd4ff..d4e1ff35 100644 --- a/V2rayNG/app/src/main/res/values-fa/strings.xml +++ b/V2rayNG/app/src/main/res/values-fa/strings.xml @@ -179,6 +179,8 @@ VPN DNS (فقط IPv4/v6) آیا VPN از شبکه محلی عبور می کند؟ + VPN Interface Address + DNS داخلی (اختیاری) DNS diff --git a/V2rayNG/app/src/main/res/values-ru/strings.xml b/V2rayNG/app/src/main/res/values-ru/strings.xml index 9b5e39f6..3caf1bd9 100644 --- a/V2rayNG/app/src/main/res/values-ru/strings.xml +++ b/V2rayNG/app/src/main/res/values-ru/strings.xml @@ -180,6 +180,8 @@ VPN DNS (только IPv4/v6) VPN пропускает LAN + VPN частный IP + Внутренняя DNS (необязательно) DNS diff --git a/V2rayNG/app/src/main/res/values-vi/strings.xml b/V2rayNG/app/src/main/res/values-vi/strings.xml index 72cdee64..fbbc17da 100644 --- a/V2rayNG/app/src/main/res/values-vi/strings.xml +++ b/V2rayNG/app/src/main/res/values-vi/strings.xml @@ -181,6 +181,8 @@ VPN DNS (Chỉ IPv4 / IPv6) Does VPN bypass LAN + VPN Interface Address + DNS nội địa (Không bắt buộc) DNS 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 a0aa7a5a..246d11c4 100644 --- a/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml +++ b/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml @@ -178,6 +178,8 @@ VPN DNS (仅支持 IPv4/v6) VPN 是否绕过局域网 + VPN 接口地址 + 境内 DNS (可选) DNS 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 e655e18f..135992f4 100644 --- a/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml +++ b/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml @@ -180,6 +180,8 @@ VPN DNS (僅支援 IPv4/v6) VPN 是否繞過區域網 + VPN 介面位址 + DNS 境内 DNS (可选) DNS hosts (格式: 網域:位址,…) @@ -360,4 +362,5 @@ 不繞過 + diff --git a/V2rayNG/app/src/main/res/values/arrays.xml b/V2rayNG/app/src/main/res/values/arrays.xml index 1863ccdd..2f03ac86 100644 --- a/V2rayNG/app/src/main/res/values/arrays.xml +++ b/V2rayNG/app/src/main/res/values/arrays.xml @@ -182,4 +182,24 @@ 2 + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + + + + 10.10.14.x + 10.1.0.x + 10.0.0.x + 172.31.0.x + 172.20.0.x + 172.16.0.x + 192.168.100.x + + \ 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 39defc59..90534f20 100644 --- a/V2rayNG/app/src/main/res/values/strings.xml +++ b/V2rayNG/app/src/main/res/values/strings.xml @@ -182,6 +182,8 @@ VPN DNS (only IPv4/v6) Does VPN bypass LAN + VPN Interface Address + Domestic DNS (Optional) DNS diff --git a/V2rayNG/app/src/main/res/xml/pref_settings.xml b/V2rayNG/app/src/main/res/xml/pref_settings.xml index cbfbb932..b9ed38f2 100644 --- a/V2rayNG/app/src/main/res/xml/pref_settings.xml +++ b/V2rayNG/app/src/main/res/xml/pref_settings.xml @@ -20,9 +20,9 @@ + android:key="pref_prefer_ipv6" + android:summary="@string/summary_pref_prefer_ipv6" + android:title="@string/title_pref_prefer_ipv6" /> + + From 33572477fc8223dfb86cd550604d266d42f0d427 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Fri, 27 Jun 2025 16:39:06 +0800 Subject: [PATCH 75/81] Adjustment setting items --- .../app/src/main/res/values-ar/strings.xml | 1 + .../app/src/main/res/values-bn/strings.xml | 1 + .../src/main/res/values-bqi-rIR/strings.xml | 1 + .../app/src/main/res/values-fa/strings.xml | 1 + .../app/src/main/res/values-ru/strings.xml | 1 + .../app/src/main/res/values-vi/strings.xml | 1 + .../src/main/res/values-zh-rCN/strings.xml | 1 + .../src/main/res/values-zh-rTW/strings.xml | 1 + V2rayNG/app/src/main/res/values/strings.xml | 1 + .../app/src/main/res/xml/pref_settings.xml | 21 +++++++++++-------- 10 files changed, 21 insertions(+), 9 deletions(-) diff --git a/V2rayNG/app/src/main/res/values-ar/strings.xml b/V2rayNG/app/src/main/res/values-ar/strings.xml index b7843b69..ba499e3d 100644 --- a/V2rayNG/app/src/main/res/values-ar/strings.xml +++ b/V2rayNG/app/src/main/res/values-ar/strings.xml @@ -141,6 +141,7 @@ الإعدادات إعدادات متقدمة + إعدادات النواة إعدادات VPN الوكيل لكل تطبيق عام: التطبيق المحدد هو وكيل، غير المحدد اتصال مباشر؛ \nوضع التجاوز: التطبيق المحدد متصل مباشرة، غير المحدد وكيل. \nخيار تحديد تطبيق الوكيل تلقائيًا في القائمة diff --git a/V2rayNG/app/src/main/res/values-bn/strings.xml b/V2rayNG/app/src/main/res/values-bn/strings.xml index 888081e5..e71fe170 100644 --- a/V2rayNG/app/src/main/res/values-bn/strings.xml +++ b/V2rayNG/app/src/main/res/values-bn/strings.xml @@ -139,6 +139,7 @@ সেটিংস এডভান্সড সেটিংস + কোর সেটিংস VPN সেটিংস প্রতি-অ্যাপ প্রক্সি সাধারণ: চেকড অ্যাপ প্রক্সি, আনচেকড সরাসরি সংযোগ; \nবাইপাস মোড: চেকড অ্যাপ সরাসরি সংযুক্ত, আনচেকড প্রক্সি। \nমেনুতে প্রক্সি অ্যাপ্লিকেশন স্বয়ংক্রিয়ভাবে নির্বাচন করার বিকল্প 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 fe0738f1..002d64df 100644 --- a/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml +++ b/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml @@ -140,6 +140,7 @@ سامووا سامووا پؽش رئڌه + سامووا هسته سامووا VPN پروکسی و ری برنومه پوی وولاتی: برنومه واجۊری بیڌه پروکسی هڌ، منپیز موستقیم بؽ نشووه هڌ. هالت دور زیڌن: برنومه نشووک ناڌه موستقیمن منپیز هڌ، پروکسی نشووک زیڌه نؽڌ. گۊزینه پسند خوتکار برنومه پروکسی من نومگه diff --git a/V2rayNG/app/src/main/res/values-fa/strings.xml b/V2rayNG/app/src/main/res/values-fa/strings.xml index d4e1ff35..b0a1bd8d 100644 --- a/V2rayNG/app/src/main/res/values-fa/strings.xml +++ b/V2rayNG/app/src/main/res/values-fa/strings.xml @@ -137,6 +137,7 @@ تنظیمات تنظیمات پیشرفته + تنظیمات هسته تنظیمات VPN پروکسی به تفکیک برنامه عمومی: برنامه انتخاب شده از طریق یک پروکسی متصل می شود، برنامه انتخاب نشده مستقیماً متصل می شود. \nحالت دور زدن: برنامه انتخاب شده مستقیماً متصل می شود، برنامه انتخاب نشده از طریق یک پروکسی متصل می شود. \nانتخاب خودکار برنامه های پراکسی در منو امکان پذیر است. diff --git a/V2rayNG/app/src/main/res/values-ru/strings.xml b/V2rayNG/app/src/main/res/values-ru/strings.xml index 3caf1bd9..4dcbdfdd 100644 --- a/V2rayNG/app/src/main/res/values-ru/strings.xml +++ b/V2rayNG/app/src/main/res/values-ru/strings.xml @@ -139,6 +139,7 @@ Настройки Расширенные настройки + Настройки ядра Настройки VPN Прокси для выбранных приложений Основной: выбранное приложение соединяется через прокси, не выбранное — напрямую;\nРежим обхода: выбранное приложение соединяется напрямую, не выбранное — через прокси.\nЕсть возможность автоматического выбора проксируемых приложений в меню. diff --git a/V2rayNG/app/src/main/res/values-vi/strings.xml b/V2rayNG/app/src/main/res/values-vi/strings.xml index fbbc17da..e7685ab0 100644 --- a/V2rayNG/app/src/main/res/values-vi/strings.xml +++ b/V2rayNG/app/src/main/res/values-vi/strings.xml @@ -138,6 +138,7 @@ Cài đặt Cài đặt nâng cao + Cài đặt lõi Cài đặt VPN Proxy theo Ứng dụng - Bình thường: Ứng dụng đã chọn sẽ kết nối thông qua Proxy, chưa chọn sẽ kết nối trực tiếp. \n- Chế độ Bypass: Ứng dụng đã chọn sẽ kết nối trực tiếp, chưa chọn sẽ kết nối qua Proxy. \n- Nếu bạn đang ở Trung Quốc thì vào Menu, chọn Tự động chọn ứng dụng Proxy. 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 246d11c4..659cb45d 100644 --- a/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml +++ b/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml @@ -137,6 +137,7 @@ 设置 进阶设置 + 核心设置 VPN 设置 分应用 常规: 勾选的 App 被代理, 未勾选的直连;\n绕行模式: 勾选的 App 直连, 未勾选的被代理.\n不明白者在菜单中选择自动选中需代理应用 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 135992f4..bd066a5b 100644 --- a/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml +++ b/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml @@ -138,6 +138,7 @@ 設定 進階 + 核心設定 VPN 設定 Proxy 個別應用程式 常規:勾選的 App 啟用 Proxy,未勾選的直接連線;\n繞行模式:勾選的 App 直接連線,未勾選的啟用 Proxy。\n可在選單中選擇自動選中需 Proxy 應用 diff --git a/V2rayNG/app/src/main/res/values/strings.xml b/V2rayNG/app/src/main/res/values/strings.xml index 90534f20..3d298755 100644 --- a/V2rayNG/app/src/main/res/values/strings.xml +++ b/V2rayNG/app/src/main/res/values/strings.xml @@ -140,6 +140,7 @@ Settings Advanced Settings + Core Settings VPN Settings Per-app proxy General: Checked apps use proxy, unchecked apps connect directly; \nBypass mode: checked apps connect directly, unchecked apps use proxy. \nThe option to automatically select proxy applications is in the menu diff --git a/V2rayNG/app/src/main/res/xml/pref_settings.xml b/V2rayNG/app/src/main/res/xml/pref_settings.xml index b9ed38f2..4a648ed8 100644 --- a/V2rayNG/app/src/main/res/xml/pref_settings.xml +++ b/V2rayNG/app/src/main/res/xml/pref_settings.xml @@ -179,9 +179,7 @@ android:title="@string/title_pref_auto_update_interval" /> - + - - + + + + + + - + + \ No newline at end of file From 777190e861cb616e764b049222850d3947623e15 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Fri, 27 Jun 2025 17:48:31 +0800 Subject: [PATCH 76/81] Added setting option for Outbound domain pre-resolve method https://github.com/2dust/v2rayNG/issues/4679 --- .../src/main/java/com/v2ray/ang/AppConfig.kt | 1 + .../src/main/java/com/v2ray/ang/fmt/FmtBase.kt | 17 +++++++++++++++-- .../com/v2ray/ang/handler/V2rayConfigManager.kt | 5 ++++- .../java/com/v2ray/ang/ui/SettingsActivity.kt | 1 + .../v2ray/ang/viewmodel/SettingsViewModel.kt | 1 + V2rayNG/app/src/main/res/values-ar/strings.xml | 7 +++++++ V2rayNG/app/src/main/res/values-bn/strings.xml | 7 +++++++ .../app/src/main/res/values-bqi-rIR/strings.xml | 7 +++++++ V2rayNG/app/src/main/res/values-fa/strings.xml | 7 +++++++ V2rayNG/app/src/main/res/values-ru/strings.xml | 7 +++++++ V2rayNG/app/src/main/res/values-vi/strings.xml | 7 +++++++ .../app/src/main/res/values-zh-rCN/strings.xml | 7 +++++++ .../app/src/main/res/values-zh-rTW/strings.xml | 6 ++++++ V2rayNG/app/src/main/res/values/arrays.xml | 6 ++++++ V2rayNG/app/src/main/res/values/strings.xml | 7 +++++++ V2rayNG/app/src/main/res/xml/pref_settings.xml | 8 ++++++++ 16 files changed, 98 insertions(+), 3 deletions(-) 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 a5d6dbdc..09e3a9d5 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/AppConfig.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/AppConfig.kt @@ -56,6 +56,7 @@ object AppConfig { const val PREF_DNS_HOSTS = "pref_dns_hosts" 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_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/fmt/FmtBase.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt index ec2d7801..73cdf958 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt @@ -4,6 +4,7 @@ import com.v2ray.ang.AppConfig import com.v2ray.ang.dto.NetworkType import com.v2ray.ang.dto.ProfileItem import com.v2ray.ang.extension.isNotNullEmpty +import com.v2ray.ang.handler.MmkvManager import com.v2ray.ang.util.HttpUtil import com.v2ray.ang.util.Utils import java.net.URI @@ -151,7 +152,19 @@ open class FmtBase { } fun getServerAddress(profileItem: ProfileItem): String { - return HttpUtil.toIdnDomain(profileItem.server.orEmpty()) - } + if (Utils.isPureIpAddress(profileItem.server.orEmpty())) { + return profileItem.server.orEmpty() + } + val domain = HttpUtil.toIdnDomain(profileItem.server.orEmpty()) + if (MmkvManager.decodeSettingsString(AppConfig.PREF_OUTBOUND_DOMAIN_RESOLVE_METHOD, "1") != "2") { + return domain + } + //Resolve and replace domain + val resolvedIps = HttpUtil.resolveHostToIP(domain, MmkvManager.decodeSettingsBool(AppConfig.PREF_PREFER_IPV6)) + if (resolvedIps.isNullOrEmpty()) { + return domain + } + return resolvedIps.first() + } } 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 ddbc78a3..fc77271e 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 @@ -131,7 +131,10 @@ object V2rayConfigManager { v2rayConfig.policy = null } - resolveOutboundDomainsToHosts(v2rayConfig) + //Resolve and add to DNS Hosts + if (MmkvManager.decodeSettingsString(AppConfig.PREF_OUTBOUND_DOMAIN_RESOLVE_METHOD, "1") == "1") { + resolveOutboundDomainsToHosts(v2rayConfig) + } result.status = true result.content = JsonUtil.toJsonPretty(v2rayConfig) ?: "" 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 515bc65f..6af64e3a 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 @@ -257,6 +257,7 @@ class SettingsActivity : BaseActivity() { AppConfig.PREF_LANGUAGE, AppConfig.PREF_UI_MODE_NIGHT, AppConfig.PREF_LOGLEVEL, + AppConfig.PREF_OUTBOUND_DOMAIN_RESOLVE_METHOD, AppConfig.PREF_MODE ).forEach { key -> if (MmkvManager.decodeSettingsString(key) != null) { 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 e12a56e5..7ac5d60f 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 @@ -49,6 +49,7 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application AppConfig.PREF_LOCAL_DNS_PORT, AppConfig.PREF_SOCKS_PORT, AppConfig.PREF_LOGLEVEL, + AppConfig.PREF_OUTBOUND_DOMAIN_RESOLVE_METHOD, AppConfig.PREF_LANGUAGE, AppConfig.PREF_UI_MODE_NIGHT, AppConfig.PREF_ROUTING_DOMAIN_STRATEGY, diff --git a/V2rayNG/app/src/main/res/values-ar/strings.xml b/V2rayNG/app/src/main/res/values-ar/strings.xml index ba499e3d..d74011e9 100644 --- a/V2rayNG/app/src/main/res/values-ar/strings.xml +++ b/V2rayNG/app/src/main/res/values-ar/strings.xml @@ -241,6 +241,7 @@ فاصل التحديث التلقائي (بالدقائق، الحد الأدنى للقيمة 15) مستوى السجل + Outbound domain pre-resolve method الوضع انقر هنا للحصول على مزيد من المساعدة اللغة @@ -357,4 +358,10 @@ Not Bypass + + Do not resolve + Resolve and add to DNS Hosts + Resolve and replace domain + + diff --git a/V2rayNG/app/src/main/res/values-bn/strings.xml b/V2rayNG/app/src/main/res/values-bn/strings.xml index e71fe170..f36c9d3a 100644 --- a/V2rayNG/app/src/main/res/values-bn/strings.xml +++ b/V2rayNG/app/src/main/res/values-bn/strings.xml @@ -241,6 +241,7 @@ অটো আপডেট ইন্টারভ্যাল (মিনিট, সর্বনিম্ন মান ১৫) লগ স্তর + Outbound domain pre-resolve method মোড আরো সাহায্যের জন্য ক্লিক করুন ভাষা @@ -362,4 +363,10 @@ Not Bypass + + Do not resolve + Resolve and add to DNS Hosts + Resolve and replace domain + + \ No newline at end of file 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 002d64df..1af31c90 100644 --- a/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml +++ b/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml @@ -241,6 +241,7 @@ فاسله ورۊ کردن خوتکار (اقلن وا 15 دؽقه بۊ) سئت داسووا + Outbound domain pre-resolve method هالت سی دووسمندیا وو هیاری بیشتر، ری ای هؽل بزݩ زۉݩ @@ -372,4 +373,10 @@ دور زیڌه نبۊ + + Do not resolve + Resolve and add to DNS Hosts + Resolve and replace domain + + diff --git a/V2rayNG/app/src/main/res/values-fa/strings.xml b/V2rayNG/app/src/main/res/values-fa/strings.xml index b0a1bd8d..081571a3 100644 --- a/V2rayNG/app/src/main/res/values-fa/strings.xml +++ b/V2rayNG/app/src/main/res/values-fa/strings.xml @@ -238,6 +238,7 @@ اشتراک های خود را به طور خودکار با فاصله زمانی در پس زمینه به روز کنید. بسته به دستگاه، این ویژگی ممکن است همیشه کار نکند. فاصله به‌ روزرسانی خودکار ( حداقل مقدار ، 15 دقیقه ) سطح گزارشات + Outbound domain pre-resolve method حالت برای اطلاعات و راهنمایی بیشتر، روی این متن کلیک کنید زبان @@ -371,4 +372,10 @@ دور زده نشود + + Do not resolve + 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 4dcbdfdd..c71054a0 100644 --- a/V2rayNG/app/src/main/res/values-ru/strings.xml +++ b/V2rayNG/app/src/main/res/values-ru/strings.xml @@ -240,6 +240,7 @@ Интервал автообновления (минут, не менее 15) Подробность ведения журнала + Outbound domain pre-resolve method Режим Нажмите для получения дополнительной информации Язык @@ -371,4 +372,10 @@ Не пропускает + + Do not resolve + Resolve and add to DNS Hosts + Resolve and replace domain + + diff --git a/V2rayNG/app/src/main/res/values-vi/strings.xml b/V2rayNG/app/src/main/res/values-vi/strings.xml index e7685ab0..86238b79 100644 --- a/V2rayNG/app/src/main/res/values-vi/strings.xml +++ b/V2rayNG/app/src/main/res/values-vi/strings.xml @@ -241,6 +241,7 @@ Thời gian cập nhật tự động (Phút, giá trị tối thiểu là 15) Cấp độ nhật ký + Outbound domain pre-resolve method Chế độ kết nối Nhấn vào đây nếu bạn cần trợ giúp! Ngôn ngữ @@ -359,4 +360,10 @@ Not Bypass + + Do not resolve + Resolve and add to DNS Hosts + Resolve and replace domain + + 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 659cb45d..a8eec856 100644 --- a/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml +++ b/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml @@ -238,6 +238,7 @@ 自动更新间隔(分钟,最小值 15) 日志级别 + Outbound 域名预解析方式 模式 点此查看更多帮助 语言 @@ -363,4 +364,10 @@ 不绕过 + + 不解析 + 解析后添加至 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 bd066a5b..f8b938c5 100644 --- a/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml +++ b/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml @@ -239,6 +239,7 @@ 自動更新間隔(分鐘,最小值 15) 記錄層級 + Outbound 網域預解析方式 模式 輕觸以檢視說明 語言 @@ -363,5 +364,10 @@ 不繞過 + + 不解析 + 解析後加入 DNS Hosts + 解析後替換原網域名稱 + diff --git a/V2rayNG/app/src/main/res/values/arrays.xml b/V2rayNG/app/src/main/res/values/arrays.xml index 2f03ac86..27f0846e 100644 --- a/V2rayNG/app/src/main/res/values/arrays.xml +++ b/V2rayNG/app/src/main/res/values/arrays.xml @@ -202,4 +202,10 @@ 192.168.100.x + + 0 + 1 + 2 + + \ 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 3d298755..57106306 100644 --- a/V2rayNG/app/src/main/res/values/strings.xml +++ b/V2rayNG/app/src/main/res/values/strings.xml @@ -242,6 +242,7 @@ Auto Update Interval (Minutes, Min value 15) Log Level + Outbound domain pre-resolve method Mode Click me for more help Language @@ -373,4 +374,10 @@ Not Bypass + + Do not resolve + Resolve and add to DNS Hosts + Resolve and replace domain + + diff --git a/V2rayNG/app/src/main/res/xml/pref_settings.xml b/V2rayNG/app/src/main/res/xml/pref_settings.xml index 4a648ed8..b5ee7aab 100644 --- a/V2rayNG/app/src/main/res/xml/pref_settings.xml +++ b/V2rayNG/app/src/main/res/xml/pref_settings.xml @@ -222,6 +222,14 @@ android:summary="%s" android:title="@string/title_core_loglevel" /> + + From 0700e834f1f5e68494fc74142e4b043840a6473c Mon Sep 17 00:00:00 2001 From: Hossein Abaspanah <63148255+hosseinabaspanah@users.noreply.github.com> Date: Fri, 27 Jun 2025 16:08:31 +0330 Subject: [PATCH 77/81] Update Luri Bakhtiari translation (#4695) --- V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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 1af31c90..21b7186f 100644 --- a/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml +++ b/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml @@ -140,7 +140,6 @@ سامووا سامووا پؽش رئڌه - سامووا هسته سامووا VPN پروکسی و ری برنومه پوی وولاتی: برنومه واجۊری بیڌه پروکسی هڌ، منپیز موستقیم بؽ نشووه هڌ. هالت دور زیڌن: برنومه نشووک ناڌه موستقیمن منپیز هڌ، پروکسی نشووک زیڌه نؽڌ. گۊزینه پسند خوتکار برنومه پروکسی من نومگه @@ -168,7 +167,7 @@ ز نوم دامنه sniffed تینا سی تور جوستن استفاڌه کۊنین وو نشۊوی مۉرد نزرن و عونوان نشۊوی IP ووردارین. ر وندن DNS مهلی - DNS پردازشت وابیڌه و دس هسته ماژول DNS (پؽشنهاڌ ابۊ، ٱر نیاز هڌ ک جوستن تور وو ولات ٱسلین دور زنی) + درخاستا DNS و هسته و من ایان وو و دست ماژول DNS پردازشت ابۊن (پؽشنهاڌ ابۊ ٱر لنگ تور جوستن سی دور زیڌن نشۊویا LAN وو وولات ٱسلی هڌین فعال بۊ) ر وندن DNS جئلی DNS مهلی نشۊویا IP جئلی ن وورگنه (زل تر، ٱما گاشڌ من یقرد ز برنومه یل کار نکونه) @@ -182,7 +181,7 @@ VPN DNS (تینا IPv4/v6) VPN ز شبکه مهلی اگوڌرته؟ - VPN Interface Address + نشۊوی رابت VPN DNS منی (اختیاری) DNS From 1f42d7fc07d4699e5af4546bf25122b6e1f6ad26 Mon Sep 17 00:00:00 2001 From: Hossein Abaspanah <63148255+hosseinabaspanah@users.noreply.github.com> Date: Fri, 27 Jun 2025 16:09:05 +0330 Subject: [PATCH 78/81] Update strings.xml (#4696) --- .../src/main/res/values-bqi-rIR/strings.xml | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) 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 21b7186f..df275ff3 100644 --- a/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml +++ b/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml @@ -175,18 +175,18 @@ ترجی IPv6 تورا IPv6 ن فعال کۊنین وو نشۊویا IPv6 ن ترجی بڌین - DNS ز ر دیر (اختیاری) (udp/tcp/https/quic) (اختیاری) + ز ر دیر (اختیاری) DNS (udp/tcp/https/quic) (اختیاری) DNS VPN DNS (تینا IPv4/v6) - VPN ز شبکه مهلی اگوڌرته؟ + ز شبکه مهلی اگوڌرته؟ VPN نشۊوی رابت VPN - DNS منی (اختیاری) + منی (اختیاری) DNS DNS - DNS هاست موستقیم (قالوو: دامنه: نشۊوی،...) + هاست موستقیم (قالوو: دامنه: نشۊوی،...) DNS دامنه:نشۊوی،... نشۊوی اینترنتی آزمایش تئخیر واقعی (http/https) @@ -206,20 +206,20 @@ پورت DNS مهلی قوۊل کردن پاک کردن کانفیگ - سی پاک وابیڌن فایل کانفیگ نیاز به قوۊل کردن دووارته ز سمت منتور هڌ + سی پاک وابیڌن فایل کانفیگ نیاز به قوۊل کردن دووارته ز سمت منتور هڌ. زی اسکنن ر ون - شؽواتگرن سی اسکن، زی مجال ر وندن بۊگۊشین، اندی ترین کودن اسکن کۊنین یا شؽواتی ن منه نوار ٱوزار پسند کۊنین. + شؽواتگرن سی اسکن، زی مجال ر وندن بۊگۊشین، ٱندی ترین کودن اسکن کۊنین یا شؽواتی ن منه نوار ٱوزار پسند کۊنین. پروکسی HTTP ن و VPN ازاف کۊنین پروکسی HTTP ن موسقیمن ز (مۊرۊرگر/ی قرد ز برنومه یل لادراری بیڌه)، بؽ استفاڌه ز دسگا NIC مجازی (Android 10+) استفاڌه ابۊ. ر وندن نشۉݩ داڌن دو سۊتۊنی - نومگه نمایه یل من دو سۊتۊن نشۉݩ داڌه ابۊن وو چینۉ ترین موئتوا بیشتری ن سیل کۊنین. سی ر وستن وا برنومه ن ز نۊ ر ونین. + نومگه نمایه یل من دو سۊتۊن نشۉݩ داڌه ابۊن وو چینۉ ترین موئتوا بیشتری ن سیل کۊنین. سی ر وستن، وا برنومه ن ز نۊ ر ونین. فشناڌن منشڌ - فشناڌن منشڌ یا داسوو موشکلا من Github + فشناڌن منشڌ یا داسووݩ موشکلا من Github ٱووڌن من جرگه تلگرام برنومه تلگرامن نجوست هریم سیخومی @@ -240,7 +240,7 @@ فاسله ورۊ کردن خوتکار (اقلن وا 15 دؽقه بۊ) سئت داسووا - Outbound domain pre-resolve method + بارت پؽش هل دامنه دری هالت سی دووسمندیا وو هیاری بیشتر، ری ای هؽل بزݩ زۉݩ @@ -373,9 +373,9 @@ - Do not resolve - Resolve and add to DNS Hosts - Resolve and replace domain + هل وو فسل مکۊنین + هل وو ٱووردن و میزبووݩ یل دامنه DNS + هل وو جایونی دامنه From 8e03de8055872c11e4e2eccfbc1817c7182b30af Mon Sep 17 00:00:00 2001 From: Hossein Abaspanah <63148255+hosseinabaspanah@users.noreply.github.com> Date: Sat, 28 Jun 2025 04:15:00 +0330 Subject: [PATCH 79/81] Update strings.xml (#4698) Add "title_core_settings" string --- V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml | 1 + 1 file changed, 1 insertion(+) 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 df275ff3..90e5cc26 100644 --- a/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml +++ b/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml @@ -140,6 +140,7 @@ سامووا سامووا پؽش رئڌه + سامووا هسته سامووا VPN پروکسی و ری برنومه پوی وولاتی: برنومه واجۊری بیڌه پروکسی هڌ، منپیز موستقیم بؽ نشووه هڌ. هالت دور زیڌن: برنومه نشووک ناڌه موستقیمن منپیز هڌ، پروکسی نشووک زیڌه نؽڌ. گۊزینه پسند خوتکار برنومه پروکسی من نومگه From 3f778a1ea239d2f6780acab734776ffe76cd242d Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Sat, 28 Jun 2025 10:02:25 +0800 Subject: [PATCH 80/81] Optimize the source of tls sni --- .../com/v2ray/ang/handler/V2rayConfigManager.kt | 14 +++++++++++--- .../com/v2ray/ang/service/V2RayServiceManager.kt | 2 +- .../app/src/main/java/com/v2ray/ang/util/Utils.kt | 15 +++++++++++++++ 3 files changed, 27 insertions(+), 4 deletions(-) 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 fc77271e..f53697bb 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 @@ -97,7 +97,7 @@ object V2rayConfigManager { val result = ConfigResult(false) val address = config.server ?: return result - if (!Utils.isIpAddress(address)) { + if (!Utils.isPureIpAddress(address)) { if (!Utils.isValidUrl(address)) { Log.w(AppConfig.TAG, "$address is an invalid ip or domain") return result @@ -154,7 +154,7 @@ object V2rayConfigManager { val result = ConfigResult(false) val address = config.server ?: return result - if (!Utils.isIpAddress(address)) { + if (!Utils.isPureIpAddress(address)) { if (!Utils.isValidUrl(address)) { Log.w(AppConfig.TAG, "$address is an invalid ip or domain") return result @@ -1052,7 +1052,15 @@ object V2rayConfigManager { fun populateTlsSettings(streamSettings: StreamSettingsBean, profileItem: ProfileItem, sniExt: String?) { val streamSecurity = profileItem.security.orEmpty() val allowInsecure = profileItem.insecure == true - val sni = if (profileItem.sni.isNullOrEmpty()) sniExt else profileItem.sni + val sni = if (profileItem.sni.isNullOrEmpty()) { + when { + sniExt.isNotNullEmpty() && Utils.isDomainName(sniExt) -> sniExt + profileItem.server.isNotNullEmpty() && Utils.isDomainName(profileItem.server) -> profileItem.server + else -> sniExt + } + } else { + profileItem.sni + } val fingerprint = profileItem.fingerPrint val alpns = profileItem.alpn val publicKey = profileItem.publicKey diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt index 659158a5..4f42ca23 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt @@ -102,7 +102,7 @@ object V2RayServiceManager { val config = MmkvManager.decodeServerConfig(guid) ?: return if (config.configType != EConfigType.CUSTOM && !Utils.isValidUrl(config.server) - && !Utils.isIpAddress(config.server) + && !Utils.isPureIpAddress(config.server.orEmpty()) ) return // val result = V2rayConfigUtil.getV2rayConfig(context, guid) // if (!result.status) return diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/util/Utils.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/util/Utils.kt index 6a61afe5..148ce4ec 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/util/Utils.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/util/Utils.kt @@ -198,6 +198,21 @@ object Utils { return isIpv4Address(value) || isIpv6Address(value) } + /** + * Check if a string is a valid domain name. + * + * A valid domain name must not be an IP address and must be a valid URL format. + * + * @param input The string to check. + * @return True if the string is a valid domain name, false otherwise. + */ + fun isDomainName(input: String?): Boolean { + if (input.isNullOrEmpty()) return false + + // Must not be an IP address and must be a valid URL format + return !isPureIpAddress(input) && isValidUrl(input) + } + /** * Check if a string is a valid IPv4 address. * From 3bf911da9c5a7c1092e3f8594e08a5ae5d29b9bc Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Sun, 29 Jun 2025 10:27:57 +0800 Subject: [PATCH 81/81] up 1.10.8 --- V2rayNG/app/build.gradle.kts | 4 ++-- V2rayNG/gradle/libs.versions.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/V2rayNG/app/build.gradle.kts b/V2rayNG/app/build.gradle.kts index 2e3ab596..1624786c 100644 --- a/V2rayNG/app/build.gradle.kts +++ b/V2rayNG/app/build.gradle.kts @@ -12,8 +12,8 @@ android { applicationId = "com.v2ray.ang" minSdk = 21 targetSdk = 35 - versionCode = 657 - versionName = "1.10.7" + versionCode = 658 + versionName = "1.10.8" multiDexEnabled = true val abiFilterList = (properties["ABI_FILTERS"] as? String)?.split(';') diff --git a/V2rayNG/gradle/libs.versions.toml b/V2rayNG/gradle/libs.versions.toml index 5bdd1236..04900e8c 100644 --- a/V2rayNG/gradle/libs.versions.toml +++ b/V2rayNG/gradle/libs.versions.toml @@ -20,7 +20,7 @@ swiperefreshlayout = "1.1.0" toasty = "1.5.2" editorkit = "2.9.0" core = "3.5.3" -workRuntimeKtx = "2.10.1" +workRuntimeKtx = "2.10.2" lifecycleViewmodelKtx = "2.9.1" multidex = "2.0.1" mockitoMockitoInline = "5.2.0"