From 06e512f5c5df5ae5fbf7bbb6fdf319935e47bb49 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Fri, 10 Jul 2026 17:46:20 +0800 Subject: [PATCH] Flatten outbound settings across protocols --- .../com/v2ray/ang/core/CoreOutboundBuilder.kt | 127 ++++++++---------- .../java/com/v2ray/ang/dto/V2rayConfig.kt | 113 ++++------------ 2 files changed, 83 insertions(+), 157 deletions(-) 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 213a090fa..a52103acf 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 @@ -64,7 +64,7 @@ object CoreOutboundBuilder { 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) { + if (protocol.equals(EConfigType.VLESS.name, true) && outbound.settings?.flow?.isNotEmpty() == true) { outbound.mux?.concurrency = -1 } } else { @@ -83,49 +83,30 @@ object CoreOutboundBuilder { fun createInitOutbound(configType: EConfigType): OutboundBean? { return when (configType) { EConfigType.VMESS, - EConfigType.VLESS -> - return OutboundBean( - protocol = configType.name.lowercase(), - settings = OutboundBean.OutSettingsBean( - vnext = listOf( - OutboundBean.OutSettingsBean.VnextBean( - users = listOf(OutboundBean.OutSettingsBean.VnextBean.UsersBean()) - ) - ) - ), - streamSettings = OutboundBean.StreamSettingsBean() - ) - + EConfigType.VLESS, EConfigType.SHADOWSOCKS, EConfigType.SOCKS, EConfigType.HTTP, - EConfigType.TROJAN -> - return OutboundBean( - protocol = configType.name.lowercase(), - settings = OutboundBean.OutSettingsBean( - servers = listOf(OutboundBean.OutSettingsBean.ServersBean()) - ), - streamSettings = OutboundBean.StreamSettingsBean() - ) + EConfigType.TROJAN -> OutboundBean( + protocol = configType.name.lowercase(), + settings = OutboundBean.OutSettingsBean(), + streamSettings = OutboundBean.StreamSettingsBean() + ) - EConfigType.WIREGUARD -> - return OutboundBean( - protocol = configType.name.lowercase(), - settings = OutboundBean.OutSettingsBean( - secretKey = "", - peers = listOf(OutboundBean.OutSettingsBean.WireGuardBean()) - ) + EConfigType.WIREGUARD -> OutboundBean( + protocol = configType.name.lowercase(), + settings = OutboundBean.OutSettingsBean( + secretKey = "", + peers = listOf(OutboundBean.OutSettingsBean.WireGuardBean()) ) + ) EConfigType.HYSTERIA, - EConfigType.HYSTERIA2 -> - return OutboundBean( - protocol = EConfigType.HYSTERIA.name.lowercase(), - settings = OutboundBean.OutSettingsBean( - servers = null - ), - streamSettings = OutboundBean.StreamSettingsBean() - ) + EConfigType.HYSTERIA2 -> OutboundBean( + protocol = EConfigType.HYSTERIA.name.lowercase(), + settings = OutboundBean.OutSettingsBean(), + streamSettings = OutboundBean.StreamSettingsBean() + ) else -> null } @@ -136,11 +117,12 @@ object CoreOutboundBuilder { private fun toOutboundVmess(profileItem: ProfileItem): OutboundBean? { val outboundBean = createInitOutbound(EConfigType.VMESS) - outboundBean?.settings?.vnext?.first()?.let { vnext -> - vnext.address = getServerAddress(profileItem) - vnext.port = profileItem.serverPort.orEmpty().toInt() - vnext.users[0].id = profileItem.password.orEmpty() - vnext.users[0].security = profileItem.method + outboundBean?.settings?.let { settings -> + settings.address = getServerAddress(profileItem) + settings.port = profileItem.serverPort.orEmpty().toInt() + settings.id = profileItem.password.orEmpty() + settings.security = profileItem.method + settings.level = AppConfig.DEFAULT_LEVEL } val sni = outboundBean?.streamSettings?.let { @@ -157,12 +139,13 @@ object CoreOutboundBuilder { private fun toOutboundVless(profileItem: ProfileItem): OutboundBean? { val outboundBean = createInitOutbound(EConfigType.VLESS) - outboundBean?.settings?.vnext?.first()?.let { vnext -> - vnext.address = getServerAddress(profileItem) - vnext.port = profileItem.serverPort.orEmpty().toInt() - vnext.users[0].id = profileItem.password.orEmpty() - vnext.users[0].encryption = profileItem.method - vnext.users[0].flow = profileItem.flow + outboundBean?.settings?.let { settings -> + settings.address = getServerAddress(profileItem) + settings.port = profileItem.serverPort.orEmpty().toInt() + settings.id = profileItem.password.orEmpty() + settings.encryption = profileItem.method + settings.flow = profileItem.flow + settings.level = AppConfig.DEFAULT_LEVEL } val sni = outboundBean?.streamSettings?.let { @@ -179,11 +162,12 @@ object CoreOutboundBuilder { private fun toOutboundShadowsocks(profileItem: ProfileItem): OutboundBean? { val outboundBean = createInitOutbound(EConfigType.SHADOWSOCKS) - outboundBean?.settings?.servers?.first()?.let { server -> - server.address = getServerAddress(profileItem) - server.port = profileItem.serverPort.orEmpty().toInt() - server.password = profileItem.password - server.method = profileItem.method + outboundBean?.settings?.let { settings -> + settings.address = getServerAddress(profileItem) + settings.port = profileItem.serverPort.orEmpty().toInt() + settings.password = profileItem.password + settings.method = profileItem.method + settings.level = AppConfig.DEFAULT_LEVEL } val sni = outboundBean?.streamSettings?.let { @@ -200,11 +184,12 @@ object CoreOutboundBuilder { private fun toOutboundTrojan(profileItem: ProfileItem): OutboundBean? { val outboundBean = createInitOutbound(EConfigType.TROJAN) - outboundBean?.settings?.servers?.first()?.let { server -> - server.address = getServerAddress(profileItem) - server.port = profileItem.serverPort.orEmpty().toInt() - server.password = profileItem.password - server.flow = profileItem.flow + outboundBean?.settings?.let { settings -> + settings.address = getServerAddress(profileItem) + settings.port = profileItem.serverPort.orEmpty().toInt() + settings.password = profileItem.password + settings.flow = profileItem.flow + settings.level = AppConfig.DEFAULT_LEVEL } val sni = outboundBean?.streamSettings?.let { @@ -221,14 +206,13 @@ object CoreOutboundBuilder { private fun toOutboundSocks(profileItem: ProfileItem): OutboundBean? { val outboundBean = createInitOutbound(EConfigType.SOCKS) - outboundBean?.settings?.servers?.first()?.let { server -> - server.address = getServerAddress(profileItem) - server.port = profileItem.serverPort.orEmpty().toInt() + outboundBean?.settings?.let { settings -> + settings.address = getServerAddress(profileItem) + settings.port = profileItem.serverPort.orEmpty().toInt() + settings.level = AppConfig.DEFAULT_LEVEL if (profileItem.username.isNotNullEmpty()) { - val socksUsersBean = OutboundBean.OutSettingsBean.ServersBean.SocksUsersBean() - socksUsersBean.user = profileItem.username.orEmpty() - socksUsersBean.pass = profileItem.password.orEmpty() - server.users = listOf(socksUsersBean) + settings.user = profileItem.username.orEmpty() + settings.pass = profileItem.password.orEmpty() } } @@ -238,14 +222,13 @@ object CoreOutboundBuilder { private fun toOutboundHttp(profileItem: ProfileItem): OutboundBean? { val outboundBean = createInitOutbound(EConfigType.HTTP) - outboundBean?.settings?.servers?.first()?.let { server -> - server.address = getServerAddress(profileItem) - server.port = profileItem.serverPort.orEmpty().toInt() + outboundBean?.settings?.let { settings -> + settings.address = getServerAddress(profileItem) + settings.port = profileItem.serverPort.orEmpty().toInt() + settings.level = AppConfig.DEFAULT_LEVEL if (profileItem.username.isNotNullEmpty()) { - val socksUsersBean = OutboundBean.OutSettingsBean.ServersBean.SocksUsersBean() - socksUsersBean.user = profileItem.username.orEmpty() - socksUsersBean.pass = profileItem.password.orEmpty() - server.users = listOf(socksUsersBean) + settings.user = profileItem.username.orEmpty() + settings.pass = profileItem.password.orEmpty() } } 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 a76ec2f4c..d7b6c13aa 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 @@ -72,65 +72,34 @@ data class V2rayConfig( var mux: MuxBean? = MuxBean(false) ) { data class OutSettingsBean( - var vnext: List? = null, - var servers: List? = null, - /*Blackhole*/ - var response: Response? = null, - /*DNS*/ - val network: String? = null, + /*Common */ var address: Any? = null, var port: Int? = null, - /*Freedom*/ - val redirect: String? = null, - val userLevel: Int? = null, - /*Loopback*/ - val inboundTag: String? = null, + var level: Int? = null, + var email: String? = null, + /*HTTP/SOCKS*/ + var user: String? = null, + var pass: String? = null, + var headers: Map? = null, + /*VMess/VLESS*/ + var id: String? = null, + var security: String? = null, + var encryption: String? = null, + /*VLESS*/ + var flow: String? = null, + /*Trojan/Shadowsocks*/ + var password: String? = null, + /*Shadowsocks*/ + var method: String? = null, + /*Hysteria/Hysteria2*/ + var version: Int? = null, /*Wireguard*/ var secretKey: String? = null, val peers: List? = null, var reserved: List? = null, var mtu: Int? = null, - var obfsPassword: String? = null, - var version: Int? = null, + var domainStrategy: String? = null, ) { - - data class VnextBean( - var address: String = "", - var port: Int = AppConfig.DEFAULT_PORT, - var users: List - ) { - - data class UsersBean( - var id: String = "", - var alterId: Int? = null, - var security: String? = null, - var level: Int = AppConfig.DEFAULT_LEVEL, - var encryption: String? = null, - var flow: String? = null - ) - } - - data class ServersBean( - var address: String = "", - var method: String? = null, - var ota: Boolean = false, - var password: String? = null, - var port: Int = AppConfig.DEFAULT_PORT, - var level: Int = AppConfig.DEFAULT_LEVEL, - val email: String? = null, - var flow: String? = null, - val ivCheck: Boolean? = null, - var users: List? = null - ) { - data class SocksUsersBean( - var user: String = "", - var pass: String = "", - var level: Int = AppConfig.DEFAULT_LEVEL - ) - } - - data class Response(var type: String) - data class WireGuardBean( var publicKey: String = "", var preSharedKey: String? = null, @@ -339,45 +308,19 @@ data class V2rayConfig( ) fun getServerAddress(): String? { - if (protocol.equals(EConfigType.VMESS.name, true) - || protocol.equals(EConfigType.VLESS.name, true) - ) { - return settings?.vnext?.firstOrNull()?.address ?: settings?.address as? String - } else 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) - ) { - return settings?.servers?.firstOrNull()?.address - } else if (protocol.equals(EConfigType.WIREGUARD.name, true)) { - return settings?.peers?.firstOrNull()?.endpoint?.substringBeforeLast(":") - } else if (protocol.equals(EConfigType.HYSTERIA2.name, true) - || protocol.equals(EConfigType.HYSTERIA.name, true) - ) { - return settings?.address as? String + return if (protocol.equals(EConfigType.WIREGUARD.name, true)) { + settings?.peers?.firstOrNull()?.endpoint?.substringBeforeLast(":") + } else { + settings?.address as? String } - return null } fun getServerPort(): Int? { - if (protocol.equals(EConfigType.VMESS.name, true) - || protocol.equals(EConfigType.VLESS.name, true) - ) { - return settings?.vnext?.firstOrNull()?.port ?: settings?.port - } else 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) - ) { - return settings?.servers?.firstOrNull()?.port - } else if (protocol.equals(EConfigType.WIREGUARD.name, true)) { - return settings?.peers?.firstOrNull()?.endpoint?.substringAfterLast(":")?.toInt() - } else if (protocol.equals(EConfigType.HYSTERIA2.name, true) - || protocol.equals(EConfigType.HYSTERIA.name, true) - ) { - return settings?.port + return if (protocol.equals(EConfigType.WIREGUARD.name, true)) { + settings?.peers?.firstOrNull()?.endpoint?.substringAfterLast(":")?.toIntOrNull() + } else { + settings?.port } - return null } fun ensureSockopt(): StreamSettingsBean.SockoptBean {