From d0f1e21f49547fcda6c212327fa8b10f9d85406b Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Fri, 1 May 2026 19:11:21 +0800 Subject: [PATCH] Move outbound global settings to builder Extract updateOutboundWithGlobalSettings from CoreConfigManager into CoreOutboundBuilder and apply it during outbound conversion. CoreConfigManager no longer calls the function (and its NetworkType import was removed); CoreOutboundBuilder.convert now runs the global-settings update and returns null if the update fails. This centralizes outbound/global preference handling and eliminates duplicated update logic across the codebase. --- .../com/v2ray/ang/core/CoreConfigManager.kt | 88 ------------------- .../com/v2ray/ang/core/CoreOutboundBuilder.kt | 85 +++++++++++++++++- 2 files changed, 83 insertions(+), 90 deletions(-) diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/core/CoreConfigManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/core/CoreConfigManager.kt index 3d4ae9d62..586447c5d 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/core/CoreConfigManager.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/core/CoreConfigManager.kt @@ -9,7 +9,6 @@ import com.v2ray.ang.dto.ProfileItem import com.v2ray.ang.dto.RulesetItem import com.v2ray.ang.dto.V2rayConfig import com.v2ray.ang.enums.EConfigType -import com.v2ray.ang.enums.NetworkType import com.v2ray.ang.extension.isNotNullEmpty import com.v2ray.ang.handler.MmkvManager import com.v2ray.ang.handler.SettingsManager @@ -249,8 +248,6 @@ object CoreConfigManager { for (config in validConfigs) { index++ val outbound = convertProfile2Outbound(config) ?: continue - val ret = updateOutboundWithGlobalSettings(outbound) - if (!ret) continue outbound.tag = "proxy-$index" outboundsList.add(outbound) } @@ -485,7 +482,6 @@ object CoreConfigManager { LogUtil.w(AppConfig.TAG, "Could not convert profile '$tag' to outbound, skipping") return@forEach } - updateOutboundWithGlobalSettings(outbound) outbound.tag = tag v2rayConfig.outbounds.add(outbound) existingTags.add(tag) @@ -796,8 +792,6 @@ object CoreConfigManager { */ 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 @@ -836,7 +830,6 @@ object CoreConfigManager { 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 @@ -848,7 +841,6 @@ object CoreConfigManager { 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" @@ -863,86 +855,6 @@ object CoreConfigManager { 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) - val protocol = outbound.protocol - if (protocol.equals(EConfigType.SHADOWSOCKS.name, true) - || protocol.equals(EConfigType.SOCKS.name, true) - || protocol.equals(EConfigType.HTTP.name, true) - || protocol.equals(EConfigType.TROJAN.name, true) - || protocol.equals(EConfigType.WIREGUARD.name, true) - || protocol.equals(EConfigType.HYSTERIA2.name, true) - || protocol.equals(EConfigType.HYSTERIA.name, true) - ) { - muxEnabled = false - } else if (outbound.streamSettings?.network == NetworkType.XHTTP.type) { - muxEnabled = false - } - - if (muxEnabled) { - outbound.mux?.enabled = true - outbound.mux?.concurrency = MmkvManager.decodeSettingsString(AppConfig.PREF_MUX_CONCURRENCY, "8").orEmpty().toInt() - outbound.mux?.xudpConcurrency = MmkvManager.decodeSettingsString(AppConfig.PREF_MUX_XUDP_CONCURRENCY, "16").orEmpty().toInt() - outbound.mux?.xudpProxyUDP443 = MmkvManager.decodeSettingsString(AppConfig.PREF_MUX_XUDP_QUIC, "reject") - if (protocol.equals(EConfigType.VLESS.name, true) && outbound.settings?.vnext?.first()?.users?.first()?.flow?.isNotEmpty() == true) { - outbound.mux?.concurrency = -1 - } - } else { - outbound.mux?.enabled = false - outbound.mux?.concurrency = -1 - } - - if (protocol.equals(EConfigType.WIREGUARD.name, true)) { - var localTunAddr = if (outbound.settings?.address == null) { - listOf(AppConfig.WIREGUARD_LOCAL_ADDRESS_V4) - } else { - outbound.settings?.address as List<*> - } - if (MmkvManager.decodeSettingsBool(AppConfig.PREF_IPV6_ENABLED) != true) { - localTunAddr = listOf(localTunAddr.first()) - } - outbound.settings?.address = localTunAddr - } - - 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 - - val requestString: String by lazy { - """{"version":"1.1","method":"GET","headers":{"User-Agent":["Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.122 Mobile Safari/537.36"],"Accept-Encoding":["gzip, deflate"],"Connection":["keep-alive"],"Pragma":"no-cache"}}""" - } - outbound.streamSettings?.tcpSettings?.header?.request = JsonUtil.fromJson( - requestString, - V2rayConfig.OutboundBean.StreamSettingsBean.TcpSettingsBean.HeaderBean.RequestBean::class.java - ) - outbound.streamSettings?.tcpSettings?.header?.request?.path = - if (path.isNullOrEmpty()) { - listOf("/") - } else { - path - } - outbound.streamSettings?.tcpSettings?.header?.request?.headers?.Host = host - } - - - } catch (e: Exception) { - LogUtil.e(AppConfig.TAG, "Failed to update outbound with global settings", e) - return false - } - return true - } - /** * Configures load balancing settings for the V2ray configuration. * diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/core/CoreOutboundBuilder.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/core/CoreOutboundBuilder.kt index 400ace5b1..4398e6268 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/core/CoreOutboundBuilder.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/core/CoreOutboundBuilder.kt @@ -5,7 +5,6 @@ import com.google.gson.JsonArray import com.google.gson.JsonObject import com.v2ray.ang.AppConfig import com.v2ray.ang.dto.ProfileItem -import com.v2ray.ang.dto.V2rayConfig import com.v2ray.ang.dto.V2rayConfig.OutboundBean import com.v2ray.ang.enums.EConfigType import com.v2ray.ang.enums.NetworkType @@ -30,7 +29,7 @@ object CoreOutboundBuilder { * and returns the resulting [OutboundBean]. */ fun convert(profileItem: ProfileItem): OutboundBean? { - return when (profileItem.configType) { + val outbound = when (profileItem.configType) { EConfigType.VMESS -> toOutboundVmess(profileItem) EConfigType.CUSTOM -> null EConfigType.SHADOWSOCKS -> toOutboundShadowsocks(profileItem) @@ -43,6 +42,88 @@ object CoreOutboundBuilder { EConfigType.POLICYGROUP -> null else -> null } + + outbound ?: return null + val ret = updateOutboundWithGlobalSettings(outbound) + if (!ret) return null + return outbound + } + + /** + * Updates outbound settings based on global preferences. + * + * Applies multiplexing and protocol-specific settings to an outbound connection. + */ + private fun updateOutboundWithGlobalSettings(outbound: OutboundBean): Boolean { + try { + var muxEnabled = MmkvManager.decodeSettingsBool(AppConfig.PREF_MUX_ENABLED, false) + val protocol = outbound.protocol + if (protocol.equals(EConfigType.SHADOWSOCKS.name, true) + || protocol.equals(EConfigType.SOCKS.name, true) + || protocol.equals(EConfigType.HTTP.name, true) + || protocol.equals(EConfigType.TROJAN.name, true) + || protocol.equals(EConfigType.WIREGUARD.name, true) + || protocol.equals(EConfigType.HYSTERIA2.name, true) + || protocol.equals(EConfigType.HYSTERIA.name, true) + ) { + muxEnabled = false + } else if (outbound.streamSettings?.network == NetworkType.XHTTP.type) { + muxEnabled = false + } + + if (muxEnabled) { + outbound.mux?.enabled = true + outbound.mux?.concurrency = MmkvManager.decodeSettingsString(AppConfig.PREF_MUX_CONCURRENCY, "8").orEmpty().toInt() + outbound.mux?.xudpConcurrency = MmkvManager.decodeSettingsString(AppConfig.PREF_MUX_XUDP_CONCURRENCY, "16").orEmpty().toInt() + outbound.mux?.xudpProxyUDP443 = MmkvManager.decodeSettingsString(AppConfig.PREF_MUX_XUDP_QUIC, "reject") + if (protocol.equals(EConfigType.VLESS.name, true) && outbound.settings?.vnext?.first()?.users?.first()?.flow?.isNotEmpty() == true) { + outbound.mux?.concurrency = -1 + } + } else { + outbound.mux?.enabled = false + outbound.mux?.concurrency = -1 + } + + if (protocol.equals(EConfigType.WIREGUARD.name, true)) { + var localTunAddr = if (outbound.settings?.address == null) { + listOf(AppConfig.WIREGUARD_LOCAL_ADDRESS_V4) + } else { + outbound.settings?.address as List<*> + } + if (MmkvManager.decodeSettingsBool(AppConfig.PREF_IPV6_ENABLED) != true) { + localTunAddr = listOf(localTunAddr.first()) + } + outbound.settings?.address = localTunAddr + } + + 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 + + val requestString: String by lazy { + """{"version":"1.1","method":"GET","headers":{"User-Agent":["Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.122 Mobile Safari/537.36"],"Accept-Encoding":["gzip, deflate"],"Connection":["keep-alive"],"Pragma":["no-cache"]}}""" + } + outbound.streamSettings?.tcpSettings?.header?.request = JsonUtil.fromJson( + requestString, + OutboundBean.StreamSettingsBean.TcpSettingsBean.HeaderBean.RequestBean::class.java + ) + outbound.streamSettings?.tcpSettings?.header?.request?.path = + if (path.isNullOrEmpty()) { + listOf("/") + } else { + path + } + outbound.streamSettings?.tcpSettings?.header?.request?.headers?.Host = host + } + + + } catch (e: Exception) { + LogUtil.e(AppConfig.TAG, "Failed to update outbound with global settings", e) + return false + } + return true } /**