From ae4a92b66bd7c16f29ebe0813a72f2fdf1a66fdd Mon Sep 17 00:00:00 2001 From: owo <93107371+Q7DF1@users.noreply.github.com> Date: Wed, 1 Apr 2026 10:12:50 +0800 Subject: [PATCH] add finalmask into share link and support import manually (#5429) * add finalmask into share link and support import manually * remove temp test code * Update V2rayNG/app/src/main/res/values/strings.xml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update V2rayNG/app/src/main/java/com/v2ray/ang/ui/ServerActivity.kt Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Make finalmask Any and parse as raw string --------- Co-authored-by: 2dust <31833384+2dust@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../java/com/v2ray/ang/dto/ProfileItem.kt | 2 +- .../java/com/v2ray/ang/dto/V2rayConfig.kt | 28 +++++++++++++++++-- .../main/java/com/v2ray/ang/fmt/FmtBase.kt | 2 ++ .../v2ray/ang/handler/V2rayConfigManager.kt | 5 +++- .../java/com/v2ray/ang/ui/ServerActivity.kt | 13 ++++++++- .../src/main/res/layout/layout_transport.xml | 21 ++++++++++++++ .../src/main/res/values-zh-rCN/strings.xml | 1 + V2rayNG/app/src/main/res/values/strings.xml | 1 + 8 files changed, 68 insertions(+), 5 deletions(-) diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/ProfileItem.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/ProfileItem.kt index 4fe07730..d2737866 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/ProfileItem.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/ProfileItem.kt @@ -36,7 +36,7 @@ data class ProfileItem( var authority: String? = null, var xhttpMode: String? = null, var xhttpExtra: String? = null, - + var finalMask: String? = null, var security: String? = null, var sni: String? = null, var alpn: String? = null, 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 0878ae45..7d75a310 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 @@ -162,7 +162,7 @@ data class V2rayConfig( var realitySettings: TlsSettingsBean? = null, var grpcSettings: GrpcSettingsBean? = null, var hysteriaSettings: HysteriaSettingsBean? = null, - var finalmask: FinalMaskBean? = null, + var finalmask: Any? = null, val dsSettings: Any? = null, var sockopt: SockoptBean? = null ) { @@ -303,9 +303,11 @@ data class V2rayConfig( ) } + //https://xtls.github.io/config/transport.html#finalmaskobject data class FinalMaskBean( var tcp: List? = null, - var udp: List? = null + var udp: List? = null, + var quicParams: QuicParamsBean? = null ) { data class MaskBean( var type: String, @@ -316,6 +318,28 @@ data class V2rayConfig( var domain: String? = null ) } + data class QuicParamsBean( + var congestion: String? = null, + var debug: Boolean? = null, + var brutalUp: String? = null, + var brutalDown: Int? = null, + var udpHop: UdpHopBean? = null, + // Using Long for large memory/window size values + var initStreamReceiveWindow: Long? = null, + var maxStreamReceiveWindow: Long? = null, + var initConnectionReceiveWindow: Long? = null, + var maxConnectionReceiveWindow: Long? = null, + var maxIdleTimeout: Int? = null, + var keepAlivePeriod: Int? = null, + var disablePathMTUDiscovery: Boolean? = null, + var maxIncomingStreams: Int? = null + ) { + // Nested data class for the udpHop JSON object + data class UdpHopBean( + var ports: String? = null, + var interval: String? = 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 b6a943e1..d3fd4772 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 @@ -67,6 +67,7 @@ open class FmtBase { config.authority = queryParam["authority"] config.xhttpMode = queryParam["mode"] config.xhttpExtra = queryParam["extra"] + config.finalMask = queryParam["fm"] config.security = queryParam["security"] if (config.security != AppConfig.TLS && config.security != AppConfig.REALITY) { @@ -110,6 +111,7 @@ open class FmtBase { config.spiderX?.nullIfBlank()?.let { dicQuery["spx"] = it } config.mldsa65Verify?.nullIfBlank()?.let { dicQuery["pqv"] = it } config.flow?.nullIfBlank()?.let { dicQuery["flow"] = it } + config.finalMask?.nullIfBlank()?.let { dicQuery["fm"] = it } // Add two keys for compatibility: "insecure" and "allowInsecure" if (config.security == AppConfig.TLS) { val insecureFlag = if (config.insecure == true) "1" else "0" 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 2aa000a7..e74e3e70 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 @@ -1174,9 +1174,12 @@ object V2rayConfigManager { val authority = profileItem.authority val xhttpMode = profileItem.xhttpMode val xhttpExtra = profileItem.xhttpExtra - + val finalMask = profileItem.finalMask var sni: String? = null streamSettings.network = transport.ifEmpty { NetworkType.TCP.type } + finalMask?.let { + streamSettings.finalmask = JsonUtil.parseString(finalMask) + } when (streamSettings.network) { NetworkType.TCP.type -> { val tcpSetting = StreamSettingsBean.TcpSettingsBean() 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 57100d20..54909e90 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 @@ -24,6 +24,7 @@ import com.v2ray.ang.dto.ProfileItem import com.v2ray.ang.enums.EConfigType import com.v2ray.ang.enums.NetworkType import com.v2ray.ang.extension.isNotNullEmpty +import com.v2ray.ang.extension.nullIfBlank import com.v2ray.ang.extension.toast import com.v2ray.ang.extension.toastSuccess import com.v2ray.ang.handler.AngConfigManager @@ -132,6 +133,7 @@ class ServerActivity : BaseActivity() { private val et_bandwidth_down: EditText? by lazy { findViewById(R.id.et_bandwidth_down) } private val et_bandwidth_up: EditText? by lazy { findViewById(R.id.et_bandwidth_up) } private val et_extra: EditText? by lazy { findViewById(R.id.et_extra) } + private val et_fm: EditText? by lazy { findViewById(R.id.et_fm) } private val layout_extra: LinearLayout? by lazy { findViewById(R.id.layout_extra) } private val et_ech_config_list: EditText? by lazy { findViewById(R.id.et_ech_config_list) } private val container_ech_config_list: LinearLayout? by lazy { findViewById(R.id.lay_ech_config_list) } @@ -241,6 +243,7 @@ class ServerActivity : BaseActivity() { else -> null }.orEmpty() ) + et_fm?.text = Utils.getEditable(config?.finalMask) layout_extra?.visibility = when (networks[position]) { @@ -489,6 +492,13 @@ class ServerActivity : BaseActivity() { } } + if (et_fm?.text?.toString().isNotNullEmpty()) { + if (JsonUtil.parseString(et_fm?.text?.toString()) == null) { + toast(R.string.server_lab_final_mask) + return false + } + } + saveCommon(config) saveStreamSettings(config) saveTls(config) @@ -557,7 +567,8 @@ class ServerActivity : BaseActivity() { profileItem.serviceName = path profileItem.authority = requestHost profileItem.xhttpMode = transportTypes(networks[network])[type] - profileItem.xhttpExtra = et_extra?.text?.toString()?.trim() + profileItem.xhttpExtra = et_extra?.text?.toString()?.trim().nullIfBlank() + profileItem.finalMask = et_fm?.text?.toString()?.trim()?.nullIfBlank() } private fun saveTls(config: ProfileItem) { diff --git a/V2rayNG/app/src/main/res/layout/layout_transport.xml b/V2rayNG/app/src/main/res/layout/layout_transport.xml index 85712ff8..07e59251 100644 --- a/V2rayNG/app/src/main/res/layout/layout_transport.xml +++ b/V2rayNG/app/src/main/res/layout/layout_transport.xml @@ -118,4 +118,25 @@ android:minLines="4" /> + + + + + + \ No newline at end of file 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 ea994f89..9259aad5 100644 --- a/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml +++ b/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml @@ -112,6 +112,7 @@ 带宽上行 (支持的单位 k/m/g/t) XHTTP 模式 XHTTP Extra 原始 JSON,格式: { XHTTPObject } + FinalMask 原始 JSON 格式: { FinalMaskObject } EchConfigList EchForceQuery 证书指纹 (SHA-256) diff --git a/V2rayNG/app/src/main/res/values/strings.xml b/V2rayNG/app/src/main/res/values/strings.xml index d5d682dd..ac5507ab 100644 --- a/V2rayNG/app/src/main/res/values/strings.xml +++ b/V2rayNG/app/src/main/res/values/strings.xml @@ -113,6 +113,7 @@ Bandwidth up (Supported units: k/m/g/t) XHTTP Mode XHTTP Extra raw JSON, format: { XHTTPObject } + finalMask raw JSON, format: { FinalMaskObject } EchConfigList EchForceQuery Certificate fingerprint (SHA-256)