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>
This commit is contained in:
owo
2026-04-01 10:12:50 +08:00
committed by GitHub
parent 55c0ccf1b0
commit ae4a92b66b
8 changed files with 68 additions and 5 deletions
@@ -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,
@@ -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<MaskBean>? = null,
var udp: List<MaskBean>? = null
var udp: List<MaskBean>? = 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
)
}
}
}
@@ -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"
@@ -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()
@@ -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) {
@@ -118,4 +118,25 @@
android:minLines="4" />
</LinearLayout>
<LinearLayout
android:id="@+id/layout_fm"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/padding_spacing_dp16"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/server_lab_final_mask" />
<EditText
android:id="@+id/et_fm"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="top"
android:inputType="textMultiLine"
android:maxLines="20"
android:minLines="4" />
</LinearLayout>
</LinearLayout>
@@ -112,6 +112,7 @@
<string name="server_lab_bandwidth_up">带宽上行 (支持的单位 k/m/g/t)</string>
<string name="server_lab_xhttp_mode">XHTTP 模式</string>
<string name="server_lab_xhttp_extra">XHTTP Extra 原始 JSON,格式: { XHTTPObject }</string>
<string name="server_lab_final_mask">FinalMask 原始 JSON 格式: { FinalMaskObject }</string>
<string name="server_lab_ech_config_list">EchConfigList</string>
<string name="server_lab_ech_force_query">EchForceQuery</string>
<string name="server_lab_pinned_ca256">证书指纹 (SHA-256)</string>
@@ -113,6 +113,7 @@
<string name="server_lab_bandwidth_up">Bandwidth up (Supported units: k/m/g/t)</string>
<string name="server_lab_xhttp_mode">XHTTP Mode</string>
<string name="server_lab_xhttp_extra">XHTTP Extra raw JSON, format: { XHTTPObject }</string>
<string name="server_lab_final_mask">finalMask raw JSON, format: { FinalMaskObject }</string>
<string name="server_lab_ech_config_list">EchConfigList</string>
<string name="server_lab_ech_force_query">EchForceQuery</string>
<string name="server_lab_pinned_ca256">Certificate fingerprint (SHA-256)</string>