diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AppConfig.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AppConfig.kt index cf866b81..0d96ac02 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AppConfig.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AppConfig.kt @@ -44,9 +44,14 @@ object AppConfig { const val PREF_MUX_CONCURRENCY = "pref_mux_concurency" const val PREF_MUX_XUDP_CONCURRENCY = "pref_mux_xudp_concurency" const val PREF_MUX_XUDP_QUIC = "pref_mux_xudp_quic" + const val PREF_FRAGMENT_ENABLED = "pref_fragment_enabled" + const val PREF_FRAGMENT_PACKETS = "pref_fragment_packets" + const val PREF_FRAGMENT_LENGTH = "pref_fragment_length" + const val PREF_FRAGMENT_INTERVAL = "pref_fragment_interval" - const val HTTP_PROTOCOL: String = "http://" - const val HTTPS_PROTOCOL: String = "https://" + const val PROTOCOL_HTTP: String = "http://" + const val PROTOCOL_HTTPS: String = "https://" + const val PROTOCOL_FREEDOM: String = "freedom" const val BROADCAST_ACTION_SERVICE = "com.v2ray.ang.action.service" const val BROADCAST_ACTION_ACTIVITY = "com.v2ray.ang.action.activity" @@ -61,6 +66,7 @@ object AppConfig { const val TAG_AGENT = "proxy" const val TAG_DIRECT = "direct" const val TAG_BLOCKED = "block" + const val TAG_FRAGMENT = "fragment" const val androidpackagenamelistUrl = "https://raw.githubusercontent.com/2dust/androidpackagenamelist/master/proxy.txt" diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/V2rayConfig.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/V2rayConfig.kt index f554e6ca..15374923 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/V2rayConfig.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/V2rayConfig.kt @@ -61,13 +61,13 @@ data class V2rayConfig( val metadataOnly: Boolean? = null) } - data class OutboundBean(val tag: String = "proxy", + data class OutboundBean(var tag: String = "proxy", var protocol: String, var settings: OutSettingsBean? = null, var streamSettings: StreamSettingsBean? = null, val proxySettings: Any? = null, val sendThrough: String? = null, - val mux: MuxBean? = MuxBean(false)) { + var mux: MuxBean? = MuxBean(false)) { data class OutSettingsBean(var vnext: List? = null, var fragment: FragmentBean? = null, @@ -141,7 +141,7 @@ data class V2rayConfig( var realitySettings: TlsSettingsBean? = null, var grpcSettings: GrpcSettingsBean? = null, val dsSettings: Any? = null, - val sockopt: SockoptBean? = null + var sockopt: SockoptBean? = null ) { data class TcpSettingsBean(var header: HeaderBean = HeaderBean(), diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SettingsActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SettingsActivity.kt index b9e1dd04..83fa9eee 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SettingsActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SettingsActivity.kt @@ -41,6 +41,10 @@ class SettingsActivity : BaseActivity() { private val muxXudpConcurrency by lazy { findPreference(AppConfig.PREF_MUX_XUDP_CONCURRENCY) } private val muxXudpQuic by lazy { findPreference(AppConfig.PREF_MUX_XUDP_QUIC) } + private val fragment by lazy { findPreference(AppConfig.PREF_FRAGMENT_ENABLED) } + private val fragmentPackets by lazy { findPreference(AppConfig.PREF_FRAGMENT_PACKETS) } + private val fragmentLength by lazy { findPreference(AppConfig.PREF_FRAGMENT_LENGTH) } + private val fragmentInterval by lazy { findPreference(AppConfig.PREF_FRAGMENT_INTERVAL) } // val autoRestart by lazy { findPreference(PREF_AUTO_RESTART) as CheckBoxPreference } private val remoteDns by lazy { findPreference(AppConfig.PREF_REMOTE_DNS) } @@ -168,6 +172,23 @@ class SettingsActivity : BaseActivity() { updateMuxXudpConcurrency(newValue as String) true } + + fragment?.setOnPreferenceChangeListener { _, newValue -> + updateFragment(newValue as Boolean) + true + } + fragmentPackets?.setOnPreferenceChangeListener { _, newValue -> + updateFragmentPackets(newValue as String) + true + } + fragmentLength?.setOnPreferenceChangeListener { _, newValue -> + updateFragmentLength(newValue as String) + true + } + fragmentInterval?.setOnPreferenceChangeListener { _, newValue -> + updateFragmentInterval(newValue as String) + true + } } override fun onStart() { @@ -184,6 +205,10 @@ class SettingsActivity : BaseActivity() { updateMux(defaultSharedPreferences.getBoolean(AppConfig.PREF_MUX_ENABLED, false)) muxConcurrency?.summary = defaultSharedPreferences.getString(AppConfig.PREF_MUX_CONCURRENCY, "8") muxXudpConcurrency?.summary = defaultSharedPreferences.getString(AppConfig.PREF_MUX_XUDP_CONCURRENCY, "8") + updateFragment(defaultSharedPreferences.getBoolean(AppConfig.PREF_FRAGMENT_ENABLED, false)) + fragmentPackets?.summary = defaultSharedPreferences.getString(AppConfig.PREF_FRAGMENT_PACKETS, "tlshello") + fragmentLength?.summary = defaultSharedPreferences.getString(AppConfig.PREF_FRAGMENT_LENGTH, "50-100") + fragmentInterval?.summary = defaultSharedPreferences.getString(AppConfig.PREF_FRAGMENT_INTERVAL, "10-20") autoUpdateInterval?.summary = defaultSharedPreferences.getString(AppConfig.SUBSCRIPTION_AUTO_UPDATE_INTERVAL,AppConfig.SUBSCRIPTION_DEFAULT_UPDATE_INTERVAL) autoUpdateInterval?.isEnabled = defaultSharedPreferences.getBoolean(AppConfig.SUBSCRIPTION_AUTO_UPDATE, false) @@ -261,9 +286,9 @@ class SettingsActivity : BaseActivity() { private fun updateMux(enabled: Boolean) { val defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity()) - muxConcurrency?.isEnabled = enabled - muxXudpConcurrency?.isEnabled = enabled - muxXudpQuic?.isEnabled = enabled + muxConcurrency?.isVisible = enabled + muxXudpConcurrency?.isVisible = enabled + muxXudpQuic?.isVisible = enabled if (enabled) { updateMuxConcurrency(defaultSharedPreferences.getString(AppConfig.PREF_MUX_CONCURRENCY, "8")) updateMuxXudpConcurrency(defaultSharedPreferences.getString(AppConfig.PREF_MUX_XUDP_CONCURRENCY, "8")) @@ -287,6 +312,27 @@ class SettingsActivity : BaseActivity() { muxXudpQuic?.isEnabled = concurrency >= 0 } } + + private fun updateFragment(enabled: Boolean) { + val defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity()) + fragmentPackets?.isVisible = enabled + fragmentLength?.isVisible = enabled + fragmentInterval?.isVisible = enabled + if (enabled) { + updateFragmentPackets(defaultSharedPreferences.getString(AppConfig.PREF_FRAGMENT_PACKETS, "tlshello")) + updateFragmentLength(defaultSharedPreferences.getString(AppConfig.PREF_FRAGMENT_LENGTH, "50-100")) + updateFragmentInterval(defaultSharedPreferences.getString(AppConfig.PREF_FRAGMENT_INTERVAL, "10-20")) + } + } + private fun updateFragmentPackets(value: String?) { + fragmentPackets?.summary = value.toString() + } + private fun updateFragmentLength(value: String?) { + fragmentLength?.summary = value.toString() + } + private fun updateFragmentInterval(value: String?) { + fragmentInterval?.summary = value.toString() + } } fun onModeHelpClicked(view: View) { diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UrlSchemeActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UrlSchemeActivity.kt index 793b3c29..165c37a6 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UrlSchemeActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UrlSchemeActivity.kt @@ -25,8 +25,8 @@ class UrlSchemeActivity : BaseActivity() { if ("text/plain" == type) { intent.getStringExtra(Intent.EXTRA_TEXT)?.let { val uri = Uri.parse(it) - if (uri.scheme?.startsWith(AppConfig.HTTPS_PROTOCOL) == true || uri.scheme?.startsWith( - AppConfig.HTTP_PROTOCOL + if (uri.scheme?.startsWith(AppConfig.PROTOCOL_HTTPS) == true || uri.scheme?.startsWith( + AppConfig.PROTOCOL_HTTP ) == true ) { val name = uri.getQueryParameter("name") ?: "Subscription" diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt index 62e725c9..6fefd03d 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt @@ -11,8 +11,8 @@ import com.google.gson.GsonBuilder import com.tencent.mmkv.MMKV import com.v2ray.ang.AppConfig import com.v2ray.ang.AppConfig.ANG_CONFIG -import com.v2ray.ang.AppConfig.HTTPS_PROTOCOL -import com.v2ray.ang.AppConfig.HTTP_PROTOCOL +import com.v2ray.ang.AppConfig.PROTOCOL_HTTPS +import com.v2ray.ang.AppConfig.PROTOCOL_HTTP import com.v2ray.ang.AppConfig.WIREGUARD_LOCAL_ADDRESS_V4 import com.v2ray.ang.AppConfig.WIREGUARD_LOCAL_MTU import com.v2ray.ang.R @@ -24,7 +24,6 @@ import java.net.URI import java.util.* import com.v2ray.ang.extension.idnHost import com.v2ray.ang.extension.removeWhiteSpace -import com.v2ray.ang.extension.toast object AngConfigManager { private val mainStorage by lazy { @@ -216,8 +215,8 @@ object AngConfigManager { } //maybe sub - if (TextUtils.isEmpty(subid) && (str.startsWith(HTTP_PROTOCOL) || str.startsWith( - HTTPS_PROTOCOL + if (TextUtils.isEmpty(subid) && (str.startsWith(PROTOCOL_HTTP) || str.startsWith( + PROTOCOL_HTTPS )) ) { MmkvManager.importUrlAsSubscription(str) diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/V2rayConfigUtil.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/V2rayConfigUtil.kt index 84ab151e..745d5af2 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/V2rayConfigUtil.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/V2rayConfigUtil.kt @@ -2,11 +2,12 @@ package com.v2ray.ang.util import android.content.Context import android.text.TextUtils -import android.util.Log import com.google.gson.* import com.tencent.mmkv.MMKV import com.v2ray.ang.AppConfig -import com.v2ray.ang.AppConfig.ANG_PACKAGE +import com.v2ray.ang.AppConfig.PROTOCOL_FREEDOM +import com.v2ray.ang.AppConfig.TAG_DIRECT +import com.v2ray.ang.AppConfig.TAG_FRAGMENT import com.v2ray.ang.AppConfig.WIREGUARD_LOCAL_ADDRESS_V4 import com.v2ray.ang.AppConfig.WIREGUARD_LOCAL_ADDRESS_V6 import com.v2ray.ang.dto.V2rayConfig @@ -16,8 +17,18 @@ import com.v2ray.ang.dto.V2rayConfig.Companion.DEFAULT_NETWORK import com.v2ray.ang.dto.V2rayConfig.Companion.HTTP object V2rayConfigUtil { - private val serverRawStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SERVER_RAW, MMKV.MULTI_PROCESS_MODE) } - private val settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) } + private val serverRawStorage by lazy { + MMKV.mmkvWithID( + MmkvManager.ID_SERVER_RAW, + MMKV.MULTI_PROCESS_MODE + ) + } + private val settingsStorage by lazy { + MMKV.mmkvWithID( + MmkvManager.ID_SETTING, + MMKV.MULTI_PROCESS_MODE + ) + } data class Result(var status: Boolean, var content: String) @@ -50,7 +61,10 @@ object V2rayConfigUtil { /** * 生成v2ray的客户端配置文件 */ - private fun getV2rayNonCustomConfig(context: Context, outbound: V2rayConfig.OutboundBean): Result { + private fun getV2rayNonCustomConfig( + context: Context, + outbound: V2rayConfig.OutboundBean + ): Result { val result = Result(false, "") //取得默认配置 val assets = Utils.readTextFromAssets(context, "v2ray_config.json") @@ -62,14 +76,15 @@ object V2rayConfigUtil { val v2rayConfig = Gson().fromJson(assets, V2rayConfig::class.java) ?: return result v2rayConfig.log.loglevel = settingsStorage?.decodeString(AppConfig.PREF_LOGLEVEL) - ?: "warning" + ?: "warning" inbounds(v2rayConfig) updateOutboundWithGlobalSettings(outbound) - v2rayConfig.outbounds[0] = outbound + updateOutboundFragment(v2rayConfig) + routing(v2rayConfig) fakedns(v2rayConfig) @@ -93,8 +108,14 @@ object V2rayConfigUtil { */ private fun inbounds(v2rayConfig: V2rayConfig): Boolean { try { - val socksPort = Utils.parseInt(settingsStorage?.decodeString(AppConfig.PREF_SOCKS_PORT), AppConfig.PORT_SOCKS.toInt()) - val httpPort = Utils.parseInt(settingsStorage?.decodeString(AppConfig.PREF_HTTP_PORT), AppConfig.PORT_HTTP.toInt()) + val socksPort = Utils.parseInt( + settingsStorage?.decodeString(AppConfig.PREF_SOCKS_PORT), + AppConfig.PORT_SOCKS.toInt() + ) + val httpPort = Utils.parseInt( + settingsStorage?.decodeString(AppConfig.PREF_HTTP_PORT), + AppConfig.PORT_HTTP.toInt() + ) v2rayConfig.inbounds.forEach { curInbound -> if (settingsStorage?.decodeBool(AppConfig.PREF_PROXY_SHARING) != true) { @@ -104,8 +125,9 @@ object V2rayConfigUtil { } v2rayConfig.inbounds[0].port = socksPort val fakedns = settingsStorage?.decodeBool(AppConfig.PREF_FAKE_DNS_ENABLED) - ?: false - val sniffAllTlsAndHttp = settingsStorage?.decodeBool(AppConfig.PREF_SNIFFING_ENABLED, true) + ?: false + val sniffAllTlsAndHttp = + settingsStorage?.decodeBool(AppConfig.PREF_SNIFFING_ENABLED, true) ?: true v2rayConfig.inbounds[0].sniffing?.enabled = fakedns || sniffAllTlsAndHttp if (!sniffAllTlsAndHttp) { @@ -131,9 +153,10 @@ object V2rayConfigUtil { } private fun fakedns(v2rayConfig: V2rayConfig) { - if (settingsStorage?.decodeBool(AppConfig.PREF_FAKE_DNS_ENABLED) == true) { + if (settingsStorage?.decodeBool(AppConfig.PREF_LOCAL_DNS_ENABLED) == true + || settingsStorage?.decodeBool(AppConfig.PREF_FAKE_DNS_ENABLED) == true) { v2rayConfig.fakedns = listOf(V2rayConfig.FakednsBean()) - v2rayConfig.outbounds.filter { it.protocol == "freedom" }.forEach { + v2rayConfig.outbounds.filter { it.protocol == PROTOCOL_FREEDOM && it.tag == TAG_DIRECT }.forEach { it.settings?.domainStrategy = "UseIP" } } @@ -144,38 +167,49 @@ object V2rayConfigUtil { */ private fun routing(v2rayConfig: V2rayConfig): Boolean { try { - routingUserRule(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_AGENT) - ?: "", AppConfig.TAG_AGENT, v2rayConfig) - routingUserRule(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT) - ?: "", AppConfig.TAG_DIRECT, v2rayConfig) - routingUserRule(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_BLOCKED) - ?: "", AppConfig.TAG_BLOCKED, v2rayConfig) + routingUserRule( + settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_AGENT) + ?: "", AppConfig.TAG_AGENT, v2rayConfig + ) + routingUserRule( + settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT) + ?: "", AppConfig.TAG_DIRECT, v2rayConfig + ) + routingUserRule( + settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_BLOCKED) + ?: "", AppConfig.TAG_BLOCKED, v2rayConfig + ) - v2rayConfig.routing.domainStrategy = settingsStorage?.decodeString(AppConfig.PREF_ROUTING_DOMAIN_STRATEGY) + v2rayConfig.routing.domainStrategy = + settingsStorage?.decodeString(AppConfig.PREF_ROUTING_DOMAIN_STRATEGY) ?: "IPIfNonMatch" // v2rayConfig.routing.domainMatcher = "mph" - val routingMode = settingsStorage?.decodeString(AppConfig.PREF_ROUTING_MODE) ?: ERoutingMode.GLOBAL_PROXY.value + val routingMode = settingsStorage?.decodeString(AppConfig.PREF_ROUTING_MODE) + ?: ERoutingMode.GLOBAL_PROXY.value // Hardcode googleapis.cn val googleapisRoute = V2rayConfig.RoutingBean.RulesBean( - type = "field", - outboundTag = AppConfig.TAG_AGENT, - domain = arrayListOf("domain:googleapis.cn") + type = "field", + outboundTag = AppConfig.TAG_AGENT, + domain = arrayListOf("domain:googleapis.cn") ) when (routingMode) { ERoutingMode.BYPASS_LAN.value -> { routingGeo("ip", "private", AppConfig.TAG_DIRECT, v2rayConfig) } + ERoutingMode.BYPASS_MAINLAND.value -> { routingGeo("", "cn", AppConfig.TAG_DIRECT, v2rayConfig) v2rayConfig.routing.rules.add(0, googleapisRoute) } + ERoutingMode.BYPASS_LAN_MAINLAND.value -> { routingGeo("ip", "private", AppConfig.TAG_DIRECT, v2rayConfig) routingGeo("", "cn", AppConfig.TAG_DIRECT, v2rayConfig) v2rayConfig.routing.rules.add(0, googleapisRoute) } + ERoutingMode.GLOBAL_DIRECT.value -> { val globalDirect = V2rayConfig.RoutingBean.RulesBean( type = "field", @@ -192,7 +226,12 @@ object V2rayConfigUtil { return true } - private fun routingGeo(ipOrDomain: String, code: String, tag: String, v2rayConfig: V2rayConfig) { + private fun routingGeo( + ipOrDomain: String, + code: String, + tag: String, + v2rayConfig: V2rayConfig + ) { try { if (!TextUtils.isEmpty(code)) { //IP @@ -277,51 +316,70 @@ object V2rayConfigUtil { try { if (settingsStorage?.decodeBool(AppConfig.PREF_FAKE_DNS_ENABLED) == true) { val geositeCn = arrayListOf("geosite:cn") - val proxyDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_AGENT) - ?: "") - val directDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT) - ?: "") + val proxyDomain = userRule2Domian( + settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_AGENT) + ?: "" + ) + val directDomain = userRule2Domian( + settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT) + ?: "" + ) // fakedns with all domains to make it always top priority - v2rayConfig.dns.servers?.add(0, - V2rayConfig.DnsBean.ServersBean(address = "fakedns", domains = geositeCn.plus(proxyDomain).plus(directDomain))) + v2rayConfig.dns.servers?.add( + 0, + V2rayConfig.DnsBean.ServersBean( + address = "fakedns", + domains = geositeCn.plus(proxyDomain).plus(directDomain) + ) + ) } // DNS inbound对象 val remoteDns = Utils.getRemoteDnsServers() if (v2rayConfig.inbounds.none { e -> e.protocol == "dokodemo-door" && e.tag == "dns-in" }) { val dnsInboundSettings = V2rayConfig.InboundBean.InSettingsBean( - address = if (Utils.isPureIpAddress(remoteDns.first())) remoteDns.first() else "1.1.1.1", - port = 53, - network = "tcp,udp") + address = if (Utils.isPureIpAddress(remoteDns.first())) remoteDns.first() else "1.1.1.1", + port = 53, + network = "tcp,udp" + ) - val localDnsPort = Utils.parseInt(settingsStorage?.decodeString(AppConfig.PREF_LOCAL_DNS_PORT), AppConfig.PORT_LOCAL_DNS.toInt()) + val localDnsPort = Utils.parseInt( + settingsStorage?.decodeString(AppConfig.PREF_LOCAL_DNS_PORT), + AppConfig.PORT_LOCAL_DNS.toInt() + ) v2rayConfig.inbounds.add( - V2rayConfig.InboundBean( - tag = "dns-in", - port = localDnsPort, - listen = "127.0.0.1", - protocol = "dokodemo-door", - settings = dnsInboundSettings, - sniffing = null)) + V2rayConfig.InboundBean( + tag = "dns-in", + port = localDnsPort, + listen = "127.0.0.1", + protocol = "dokodemo-door", + settings = dnsInboundSettings, + sniffing = null + ) + ) } // DNS outbound对象 if (v2rayConfig.outbounds.none { e -> e.protocol == "dns" && e.tag == "dns-out" }) { v2rayConfig.outbounds.add( - V2rayConfig.OutboundBean( - protocol = "dns", - tag = "dns-out", - settings = null, - streamSettings = null, - mux = null)) + V2rayConfig.OutboundBean( + protocol = "dns", + tag = "dns-out", + settings = null, + streamSettings = null, + mux = null + ) + ) } // DNS routing tag - v2rayConfig.routing.rules.add(0, V2rayConfig.RoutingBean.RulesBean( + v2rayConfig.routing.rules.add( + 0, V2rayConfig.RoutingBean.RulesBean( type = "field", inboundTag = arrayListOf("dns-in"), outboundTag = "dns-out", - domain = null) + domain = null + ) ) } catch (e: Exception) { e.printStackTrace() @@ -335,43 +393,73 @@ object V2rayConfigUtil { val hosts = mutableMapOf() val servers = ArrayList() val remoteDns = Utils.getRemoteDnsServers() - val proxyDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_AGENT) - ?: "") + val proxyDomain = userRule2Domian( + settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_AGENT) + ?: "" + ) remoteDns.forEach { servers.add(it) } if (proxyDomain.size > 0) { - servers.add(V2rayConfig.DnsBean.ServersBean(remoteDns.first(), 53, proxyDomain, null)) + servers.add( + V2rayConfig.DnsBean.ServersBean( + remoteDns.first(), + 53, + proxyDomain, + null + ) + ) } // domestic DNS - val directDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT) - ?: "") - val routingMode = settingsStorage?.decodeString(AppConfig.PREF_ROUTING_MODE) ?: ERoutingMode.GLOBAL_PROXY.value + val directDomain = userRule2Domian( + settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT) + ?: "" + ) + val routingMode = settingsStorage?.decodeString(AppConfig.PREF_ROUTING_MODE) + ?: ERoutingMode.GLOBAL_PROXY.value if (directDomain.size > 0 || routingMode == ERoutingMode.BYPASS_MAINLAND.value || routingMode == ERoutingMode.BYPASS_LAN_MAINLAND.value) { val domesticDns = Utils.getDomesticDnsServers() val geositeCn = arrayListOf("geosite:cn") val geoipCn = arrayListOf("geoip:cn") if (directDomain.size > 0) { - servers.add(V2rayConfig.DnsBean.ServersBean(domesticDns.first(), 53, directDomain, geoipCn)) + servers.add( + V2rayConfig.DnsBean.ServersBean( + domesticDns.first(), + 53, + directDomain, + geoipCn + ) + ) } if (routingMode == ERoutingMode.BYPASS_MAINLAND.value || routingMode == ERoutingMode.BYPASS_LAN_MAINLAND.value) { - servers.add(V2rayConfig.DnsBean.ServersBean(domesticDns.first(), 53, geositeCn, geoipCn)) + servers.add( + V2rayConfig.DnsBean.ServersBean( + domesticDns.first(), + 53, + geositeCn, + geoipCn + ) + ) } if (Utils.isPureIpAddress(domesticDns.first())) { - v2rayConfig.routing.rules.add(0, V2rayConfig.RoutingBean.RulesBean( + v2rayConfig.routing.rules.add( + 0, V2rayConfig.RoutingBean.RulesBean( type = "field", outboundTag = AppConfig.TAG_DIRECT, port = "53", ip = arrayListOf(domesticDns.first()), - domain = null) + domain = null + ) ) } } - val blkDomain = userRule2Domian(settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_BLOCKED) - ?: "") + val blkDomain = userRule2Domian( + settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_BLOCKED) + ?: "" + ) if (blkDomain.size > 0) { hosts.putAll(blkDomain.map { it to "127.0.0.1" }) } @@ -381,17 +469,20 @@ object V2rayConfigUtil { // DNS dns对象 v2rayConfig.dns = V2rayConfig.DnsBean( - servers = servers, - hosts = hosts) + servers = servers, + hosts = hosts + ) // DNS routing if (Utils.isPureIpAddress(remoteDns.first())) { - v2rayConfig.routing.rules.add(0, V2rayConfig.RoutingBean.RulesBean( + v2rayConfig.routing.rules.add( + 0, V2rayConfig.RoutingBean.RulesBean( type = "field", outboundTag = AppConfig.TAG_AGENT, port = "53", ip = arrayListOf(remoteDns.first()), - domain = null) + domain = null + ) ) } } catch (e: Exception) { @@ -463,6 +554,7 @@ object V2rayConfigUtil { outbound.streamSettings?.tcpSettings?.header?.request?.headers?.Host = host!! } + } catch (e: Exception) { e.printStackTrace() return false @@ -470,4 +562,39 @@ object V2rayConfigUtil { return true } + private fun updateOutboundFragment(v2rayConfig: V2rayConfig): Boolean { + try { + if (settingsStorage?.decodeBool(AppConfig.PREF_FRAGMENT_ENABLED, false) == true) { + val fragmentOutbound = + V2rayConfig.OutboundBean(protocol = PROTOCOL_FREEDOM, tag = TAG_FRAGMENT, mux = null) + fragmentOutbound.settings = V2rayConfig.OutboundBean.OutSettingsBean( + fragment = V2rayConfig.OutboundBean.OutSettingsBean.FragmentBean( + packets = settingsStorage?.decodeString(AppConfig.PREF_FRAGMENT_PACKETS) + ?: "tlshello", + length = settingsStorage?.decodeString(AppConfig.PREF_FRAGMENT_LENGTH) + ?: "50-100", + interval = settingsStorage?.decodeString(AppConfig.PREF_FRAGMENT_INTERVAL) + ?: "10-20" + ) + ) + fragmentOutbound.streamSettings = V2rayConfig.OutboundBean.StreamSettingsBean( + sockopt = V2rayConfig.OutboundBean.StreamSettingsBean.SockoptBean( + TcpNoDelay = true, + mark = 255 + ) + ) + v2rayConfig.outbounds.add(fragmentOutbound) + + //proxy chain + v2rayConfig.outbounds[0].streamSettings?.sockopt = + V2rayConfig.OutboundBean.StreamSettingsBean.SockoptBean( + dialerProxy = TAG_FRAGMENT + ) + } + } catch (e: Exception) { + e.printStackTrace() + return false + } + return true + } } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/SettingsViewModel.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/SettingsViewModel.kt index 98230e61..d112dd36 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/SettingsViewModel.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/SettingsViewModel.kt @@ -41,6 +41,9 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application AppConfig.PREF_V2RAY_ROUTING_BLOCKED, AppConfig.PREF_V2RAY_ROUTING_DIRECT, AppConfig.SUBSCRIPTION_AUTO_UPDATE_INTERVAL, + AppConfig.PREF_FRAGMENT_PACKETS, + AppConfig.PREF_FRAGMENT_LENGTH, + AppConfig.PREF_FRAGMENT_INTERVAL, AppConfig.PREF_MUX_XUDP_QUIC, -> { settingsStorage?.encode(key, sharedPreferences.getString(key, "")) } @@ -55,6 +58,7 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application AppConfig.PREF_CONFIRM_REMOVE, AppConfig.PREF_START_SCAN_IMMEDIATE, AppConfig.SUBSCRIPTION_AUTO_UPDATE, + AppConfig.PREF_FRAGMENT_ENABLED, AppConfig.PREF_MUX_ENABLED, -> { settingsStorage?.encode(key, sharedPreferences.getBoolean(key, false)) } diff --git a/V2rayNG/app/src/main/res/values-vi/strings.xml b/V2rayNG/app/src/main/res/values-vi/strings.xml index 0b4d8bcd..cca31901 100644 --- a/V2rayNG/app/src/main/res/values-vi/strings.xml +++ b/V2rayNG/app/src/main/res/values-vi/strings.xml @@ -103,6 +103,7 @@ 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. + Mux Settings Bật Mux Giảm độ trễ trong bước bắt tay của kết nối TCP. Mux phân phối dữ liệu từ nhiều kết nối TCP trên một kết nối TCP duy nhất. Không nên sử dụng Mux để xem video, download file hoặc chạy speedtest vì thường không hiệu quả. TCP connections (từ 1 đến 1024) 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 00d0f539..5208b864 100644 --- a/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml +++ b/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml @@ -103,6 +103,7 @@ 分应用代理 常规:勾选的App被代理,未勾选的直连;\n绕行模式:勾选的App直连,未勾选的被代理.\n不明白者在菜单中选择自动选中需代理应用 + Mux 多路复用 设置 启用 Mux 多路复用 减低延时,但可能会断流,建议不要启用。\nTCP,UDP 及 QUIC 流量处理方式下方可选。 TCP 复用子链接数(可填 -1 至 1024) @@ -225,6 +226,16 @@ "已连接,点击测试连接" "未连接" + 订阅导入成功 + 导入订阅失败 + 添加 + 添加链接 + 分片(fragment) 设置 + 分片方式 + 分片包长(最小-最大) + 分片间隔(最小-最大) + 启用分片(fragment) + 二维码 导出至剪贴板 @@ -255,9 +266,5 @@ VPN 仅代理 - 订阅导入成功 - 导入订阅失败 - 添加 - 添加链接 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 c81b140e..d865928d 100644 --- a/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml +++ b/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml @@ -103,6 +103,7 @@ Proxy 個別應用程式 常規:勾選的 App 啟用 Proxy,未勾選的直接連線;\n繞行模式:勾選的 App 直接連線,未勾選的啟用 Proxy。\n可在選單中選擇自動選中需 Proxy 應用 + Mux 設定 啟用 Mux 多路復用 減低延時 但可能會斷流\nTCP,UDP 及 QUIC 流量處理方式下方可選 TCP 復用子鏈接數(可填 -1 至 1024) @@ -225,6 +226,16 @@ "已連線,輕觸以檢查連線能力" "未連線" + 訂閱導入成功 + 導入訂閱失敗 + 添加 + 添加連結 + 分片(fragment) 設定 + 分片方式 + 分片包长(最小-最大) + 分片间隔(最小-最大) + 启用分片(fragment) + QR Code 匯出至剪貼簿 @@ -254,9 +265,5 @@ VPN 僅 Proxy - 訂閱導入成功 - 導入訂閱失敗 - 添加 - 添加連結 diff --git a/V2rayNG/app/src/main/res/values/arrays.xml b/V2rayNG/app/src/main/res/values/arrays.xml index 38a20734..c64f0fb3 100644 --- a/V2rayNG/app/src/main/res/values/arrays.xml +++ b/V2rayNG/app/src/main/res/values/arrays.xml @@ -60,6 +60,10 @@ reality + + tlshello + + chrome diff --git a/V2rayNG/app/src/main/res/values/strings.xml b/V2rayNG/app/src/main/res/values/strings.xml index a518741c..287723d9 100644 --- a/V2rayNG/app/src/main/res/values/strings.xml +++ b/V2rayNG/app/src/main/res/values/strings.xml @@ -114,6 +114,7 @@ Per-app proxy General: Checked App is proxy, unchecked direct connection; \nbypass mode: checked app directly connected, unchecked proxy. \nThe option to automatically select the proxy application in the menu + Mux Settings Enable Mux Faster, but it may cause unstable connectivity\ncustomize how to handle TCP, UDP and QUIC below TCP connections(range -1 to 1024) @@ -240,6 +241,11 @@ Subscription imported Successfully Import subscription failed + Fragment Settings + Fragment Packets + Fragment Length (min-max) + Fragment Interval (min-max) + Enable Fragment QRcode diff --git a/V2rayNG/app/src/main/res/xml/pref_settings.xml b/V2rayNG/app/src/main/res/xml/pref_settings.xml index b4793db9..51b2c6fd 100644 --- a/V2rayNG/app/src/main/res/xml/pref_settings.xml +++ b/V2rayNG/app/src/main/res/xml/pref_settings.xml @@ -7,31 +7,6 @@ android:summary="@string/summary_pref_sniffing_enabled" android:title="@string/title_pref_sniffing_enabled" /> - - - - - - - - + + + + + + + + + + + + + + + + + + + +