diff --git a/app/schemas/io.nekohasekai.sagernet.database.SagerDatabase/37.json b/app/schemas/io.nekohasekai.sagernet.database.SagerDatabase/37.json
new file mode 100644
index 00000000000..0a08035367e
--- /dev/null
+++ b/app/schemas/io.nekohasekai.sagernet.database.SagerDatabase/37.json
@@ -0,0 +1,475 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 37,
+ "identityHash": "286ab111b45c46a16635823c2396e034",
+ "entities": [
+ {
+ "tableName": "proxy_groups",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userOrder` INTEGER NOT NULL, `ungrouped` INTEGER NOT NULL, `name` TEXT, `type` INTEGER NOT NULL, `subscription` BLOB, `order` INTEGER NOT NULL, `frontProxy` INTEGER NOT NULL DEFAULT -1, `landingProxy` INTEGER NOT NULL DEFAULT -1, `utlsFingerprintForTLS` TEXT NOT NULL DEFAULT '', `utlsFingerprintForReality` TEXT NOT NULL DEFAULT '')",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userOrder",
+ "columnName": "userOrder",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "ungrouped",
+ "columnName": "ungrouped",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subscription",
+ "columnName": "subscription",
+ "affinity": "BLOB"
+ },
+ {
+ "fieldPath": "order",
+ "columnName": "order",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "frontProxy",
+ "columnName": "frontProxy",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "-1"
+ },
+ {
+ "fieldPath": "landingProxy",
+ "columnName": "landingProxy",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "-1"
+ },
+ {
+ "fieldPath": "utlsFingerprintForTLS",
+ "columnName": "utlsFingerprintForTLS",
+ "affinity": "TEXT",
+ "notNull": true,
+ "defaultValue": "''"
+ },
+ {
+ "fieldPath": "utlsFingerprintForReality",
+ "columnName": "utlsFingerprintForReality",
+ "affinity": "TEXT",
+ "notNull": true,
+ "defaultValue": "''"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ }
+ },
+ {
+ "tableName": "proxy_entities",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `groupId` INTEGER NOT NULL, `type` INTEGER NOT NULL, `userOrder` INTEGER NOT NULL, `tx` INTEGER NOT NULL, `rx` INTEGER NOT NULL, `status` INTEGER NOT NULL, `ping` INTEGER NOT NULL, `uuid` TEXT NOT NULL, `error` TEXT, `socksBean` BLOB, `httpBean` BLOB, `ssBean` BLOB, `ssrBean` BLOB, `vmessBean` BLOB, `vlessBean` BLOB, `trojanBean` BLOB, `naiveBean` BLOB, `hysteria2Bean` BLOB, `mieruBean` BLOB, `tuic5Bean` BLOB, `sshBean` BLOB, `wgBean` BLOB, `juicityBean` BLOB, `http3Bean` BLOB, `anytlsBean` BLOB, `shadowquicBean` BLOB, `trustTunnelBean` BLOB, `configBean` BLOB, `chainBean` BLOB, `balancerBean` BLOB)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "groupId",
+ "columnName": "groupId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userOrder",
+ "columnName": "userOrder",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "tx",
+ "columnName": "tx",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "rx",
+ "columnName": "rx",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "status",
+ "columnName": "status",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "ping",
+ "columnName": "ping",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "uuid",
+ "columnName": "uuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "error",
+ "columnName": "error",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "socksBean",
+ "columnName": "socksBean",
+ "affinity": "BLOB"
+ },
+ {
+ "fieldPath": "httpBean",
+ "columnName": "httpBean",
+ "affinity": "BLOB"
+ },
+ {
+ "fieldPath": "ssBean",
+ "columnName": "ssBean",
+ "affinity": "BLOB"
+ },
+ {
+ "fieldPath": "ssrBean",
+ "columnName": "ssrBean",
+ "affinity": "BLOB"
+ },
+ {
+ "fieldPath": "vmessBean",
+ "columnName": "vmessBean",
+ "affinity": "BLOB"
+ },
+ {
+ "fieldPath": "vlessBean",
+ "columnName": "vlessBean",
+ "affinity": "BLOB"
+ },
+ {
+ "fieldPath": "trojanBean",
+ "columnName": "trojanBean",
+ "affinity": "BLOB"
+ },
+ {
+ "fieldPath": "naiveBean",
+ "columnName": "naiveBean",
+ "affinity": "BLOB"
+ },
+ {
+ "fieldPath": "hysteria2Bean",
+ "columnName": "hysteria2Bean",
+ "affinity": "BLOB"
+ },
+ {
+ "fieldPath": "mieruBean",
+ "columnName": "mieruBean",
+ "affinity": "BLOB"
+ },
+ {
+ "fieldPath": "tuic5Bean",
+ "columnName": "tuic5Bean",
+ "affinity": "BLOB"
+ },
+ {
+ "fieldPath": "sshBean",
+ "columnName": "sshBean",
+ "affinity": "BLOB"
+ },
+ {
+ "fieldPath": "wgBean",
+ "columnName": "wgBean",
+ "affinity": "BLOB"
+ },
+ {
+ "fieldPath": "juicityBean",
+ "columnName": "juicityBean",
+ "affinity": "BLOB"
+ },
+ {
+ "fieldPath": "http3Bean",
+ "columnName": "http3Bean",
+ "affinity": "BLOB"
+ },
+ {
+ "fieldPath": "anytlsBean",
+ "columnName": "anytlsBean",
+ "affinity": "BLOB"
+ },
+ {
+ "fieldPath": "shadowquicBean",
+ "columnName": "shadowquicBean",
+ "affinity": "BLOB"
+ },
+ {
+ "fieldPath": "trustTunnelBean",
+ "columnName": "trustTunnelBean",
+ "affinity": "BLOB"
+ },
+ {
+ "fieldPath": "configBean",
+ "columnName": "configBean",
+ "affinity": "BLOB"
+ },
+ {
+ "fieldPath": "chainBean",
+ "columnName": "chainBean",
+ "affinity": "BLOB"
+ },
+ {
+ "fieldPath": "balancerBean",
+ "columnName": "balancerBean",
+ "affinity": "BLOB"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [
+ {
+ "name": "groupId",
+ "unique": false,
+ "columnNames": [
+ "groupId"
+ ],
+ "orders": [],
+ "createSql": "CREATE INDEX IF NOT EXISTS `groupId` ON `${TABLE_NAME}` (`groupId`)"
+ }
+ ]
+ },
+ {
+ "tableName": "rules",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `userOrder` INTEGER NOT NULL, `enabled` INTEGER NOT NULL, `domains` TEXT NOT NULL, `ip` TEXT NOT NULL, `port` TEXT NOT NULL, `sourcePort` TEXT NOT NULL, `network` TEXT NOT NULL, `source` TEXT NOT NULL, `protocol` TEXT NOT NULL, `attrs` TEXT NOT NULL, `outbound` INTEGER NOT NULL, `packages` TEXT NOT NULL, `ssid` TEXT NOT NULL DEFAULT '', `networkType` TEXT NOT NULL DEFAULT '', `customPackageNames` TEXT NOT NULL DEFAULT '')",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userOrder",
+ "columnName": "userOrder",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "enabled",
+ "columnName": "enabled",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "domains",
+ "columnName": "domains",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "ip",
+ "columnName": "ip",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "port",
+ "columnName": "port",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "sourcePort",
+ "columnName": "sourcePort",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "network",
+ "columnName": "network",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "source",
+ "columnName": "source",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "protocol",
+ "columnName": "protocol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "attrs",
+ "columnName": "attrs",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "outbound",
+ "columnName": "outbound",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "packages",
+ "columnName": "packages",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "ssid",
+ "columnName": "ssid",
+ "affinity": "TEXT",
+ "notNull": true,
+ "defaultValue": "''"
+ },
+ {
+ "fieldPath": "networkType",
+ "columnName": "networkType",
+ "affinity": "TEXT",
+ "notNull": true,
+ "defaultValue": "''"
+ },
+ {
+ "fieldPath": "customPackageNames",
+ "columnName": "customPackageNames",
+ "affinity": "TEXT",
+ "notNull": true,
+ "defaultValue": "''"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ }
+ },
+ {
+ "tableName": "trafficStats",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `uid` INTEGER NOT NULL, `tcpConnections` INTEGER NOT NULL, `udpConnections` INTEGER NOT NULL, `uplink` INTEGER NOT NULL, `downlink` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "uid",
+ "columnName": "uid",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "tcpConnections",
+ "columnName": "tcpConnections",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "udpConnections",
+ "columnName": "udpConnections",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "uplink",
+ "columnName": "uplink",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "downlink",
+ "columnName": "downlink",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ }
+ },
+ {
+ "tableName": "assets",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `name` TEXT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "url",
+ "columnName": "url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ }
+ }
+ ],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '286ab111b45c46a16635823c2396e034')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/nekohasekai/sagernet/Constants.kt b/app/src/main/java/io/nekohasekai/sagernet/Constants.kt
index a5228b63275..cd59471f7ff 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/Constants.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/Constants.kt
@@ -295,6 +295,8 @@ object Key {
const val GROUP_LANDING_PROXY_OUTBOUND = "groupLandingOutbound"
const val GROUP_FRONT_PROXY = "groupFrontProxy"
const val GROUP_LANDING_PROXY = "groupLandingProxy"
+ const val GROUP_UTLS_FINGERPRINT_FOR_TLS = "groupUTLSFingerprintForTLS"
+ const val GROUP_UTLS_FINGERPRINT_FOR_REALITY = "groupUTLSFingerprintForReality"
const val GROUP_SUBSCRIPTION = "groupSubscription"
const val SUBSCRIPTION_TYPE = "subscriptionType"
diff --git a/app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt b/app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt
index 50c3a5af3b0..90dc839c49d 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt
@@ -436,6 +436,8 @@ object DataStore : OnPreferenceDataStoreChangeListener {
var landingProxyOutbound by profileCacheStore.long(Key.GROUP_LANDING_PROXY_OUTBOUND)
var frontProxy by profileCacheStore.stringToInt(Key.GROUP_FRONT_PROXY)
var landingProxy by profileCacheStore.stringToInt(Key.GROUP_LANDING_PROXY)
+ var groupUTLSFingerprintForTLS by profileCacheStore.string(Key.GROUP_UTLS_FINGERPRINT_FOR_TLS)
+ var groupUTLSFingerprintForReality by profileCacheStore.string(Key.GROUP_UTLS_FINGERPRINT_FOR_REALITY)
var serverConfig by profileCacheStore.string(Key.SERVER_CONFIG)
diff --git a/app/src/main/java/io/nekohasekai/sagernet/database/ProxyGroup.kt b/app/src/main/java/io/nekohasekai/sagernet/database/ProxyGroup.kt
index 977450d40eb..7d3ab70aa42 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/database/ProxyGroup.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/database/ProxyGroup.kt
@@ -39,7 +39,9 @@ data class ProxyGroup(
var subscription: SubscriptionBean? = null,
var order: Int = GroupOrder.ORIGIN,
@ColumnInfo(defaultValue = (-1L).toString()) var frontProxy: Long = -1L,
- @ColumnInfo(defaultValue = (-1L).toString()) var landingProxy: Long = -1L
+ @ColumnInfo(defaultValue = (-1L).toString()) var landingProxy: Long = -1L,
+ @ColumnInfo(defaultValue = "") var utlsFingerprintForTLS: String = "",
+ @ColumnInfo(defaultValue = "") var utlsFingerprintForReality: String = "",
) : Serializable() {
@Transient
@@ -57,7 +59,7 @@ data class ProxyGroup(
val subscription = subscription!!
subscription.serializeForShare(output)
} else {
- output.writeInt(1)
+ output.writeInt(2)
output.writeLong(id)
output.writeLong(userOrder)
output.writeBoolean(ungrouped)
@@ -69,6 +71,8 @@ data class ProxyGroup(
output.writeInt(order)
output.writeLong(frontProxy)
output.writeLong(landingProxy)
+ output.writeString(utlsFingerprintForTLS)
+ output.writeString(utlsFingerprintForReality)
}
}
@@ -97,6 +101,10 @@ data class ProxyGroup(
frontProxy = input.readLong()
landingProxy = input.readLong()
}
+ if (version >= 2) {
+ utlsFingerprintForTLS = input.readString()
+ utlsFingerprintForReality = input.readString()
+ }
}
}
diff --git a/app/src/main/java/io/nekohasekai/sagernet/database/SagerDatabase.kt b/app/src/main/java/io/nekohasekai/sagernet/database/SagerDatabase.kt
index a941276e17f..f7d1cc0f2e6 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/database/SagerDatabase.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/database/SagerDatabase.kt
@@ -29,7 +29,7 @@ import kotlinx.coroutines.launch
@Database(
entities = [ProxyGroup::class, ProxyEntity::class, RuleEntity::class, StatsEntity::class, AssetEntity::class],
- version = 36,
+ version = 37,
autoMigrations = [AutoMigration(
from = 12,
to = 14,
@@ -105,6 +105,9 @@ import kotlinx.coroutines.launch
from = 35,
to = 36,
spec = SagerDatabase_Migration_35_36::class
+ ), AutoMigration(
+ from = 36,
+ to = 37,
)]
)
@TypeConverters(value = [KryoConverters::class, GsonConverters::class])
diff --git a/app/src/main/java/io/nekohasekai/sagernet/fmt/ConfigBuilder.kt b/app/src/main/java/io/nekohasekai/sagernet/fmt/ConfigBuilder.kt
index 4b1a06edf4c..bbde85c0348 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/fmt/ConfigBuilder.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/fmt/ConfigBuilder.kt
@@ -602,6 +602,10 @@ fun buildV2RayConfig(
val bean = proxyEntity.requireBean()
currentOutbound = OutboundObject()
+ val proxyGroup = SagerDatabase.groupDao.getById(proxyEntity.groupId)
+ val groupUTLSFingerprintForTLS = proxyGroup?.utlsFingerprintForTLS ?: ""
+ val groupUTLSFingerprintForReality = proxyGroup?.utlsFingerprintForReality ?: ""
+
val tagIn: String
var needGlobal: Boolean
@@ -888,6 +892,8 @@ fun buildV2RayConfig(
val overrideFingerprint = DataStore.experimentalFlagsProperties.getProperty("overrideUTLSFingerprintForTLS")
if (!overrideFingerprint.isNullOrEmpty()) {
fingerprint = overrideFingerprint
+ } else if (!groupUTLSFingerprintForTLS.isNotEmpty()) {
+ fingerprint = groupUTLSFingerprintForTLS
} else if (bean.utlsFingerprint.isNotEmpty()) {
fingerprint = bean.utlsFingerprint
}
@@ -918,6 +924,8 @@ fun buildV2RayConfig(
val overrideFingerprint = DataStore.experimentalFlagsProperties.getProperty("overrideUTLSFingerprintForREALITY")
if (!overrideFingerprint.isNullOrEmpty()) {
fingerprint = overrideFingerprint
+ } else if (groupUTLSFingerprintForReality.isNotEmpty()) {
+ fingerprint = groupUTLSFingerprintForReality
} else if (bean.realityFingerprint.isNotEmpty()) {
fingerprint = bean.realityFingerprint
}
@@ -1616,6 +1624,8 @@ fun buildV2RayConfig(
val overrideFingerprint = DataStore.experimentalFlagsProperties.getProperty("overrideUTLSFingerprintForTLS")
if (!overrideFingerprint.isNullOrEmpty()) {
fingerprint = overrideFingerprint
+ } else if (groupUTLSFingerprintForTLS.isNotEmpty()) {
+ fingerprint = groupUTLSFingerprintForTLS
} else if (bean.utlsFingerprint.isNotEmpty()) {
fingerprint = bean.utlsFingerprint
}
@@ -1643,6 +1653,8 @@ fun buildV2RayConfig(
val overrideFingerprint = DataStore.experimentalFlagsProperties.getProperty("overrideUTLSFingerprintForREALITY")
if (!overrideFingerprint.isNullOrEmpty()) {
fingerprint = overrideFingerprint
+ } else if (groupUTLSFingerprintForReality.isNotEmpty()) {
+ fingerprint = groupUTLSFingerprintForReality
} else if (bean.realityFingerprint.isNotEmpty()) {
fingerprint = bean.realityFingerprint
}
@@ -1833,6 +1845,8 @@ fun buildV2RayConfig(
val overrideFingerprint = DataStore.experimentalFlagsProperties.getProperty("overrideUTLSFingerprintForTLS")
if (!overrideFingerprint.isNullOrEmpty()) {
fingerprint = overrideFingerprint
+ } else if (groupUTLSFingerprintForTLS.isNotEmpty()) {
+ fingerprint = groupUTLSFingerprintForTLS
} else if (bean.utlsFingerprint.isNotEmpty()) {
fingerprint = bean.utlsFingerprint
}
diff --git a/app/src/main/java/io/nekohasekai/sagernet/ui/GroupSettingsActivity.kt b/app/src/main/java/io/nekohasekai/sagernet/ui/GroupSettingsActivity.kt
index 526d99614ba..468f57812f6 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/ui/GroupSettingsActivity.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/ui/GroupSettingsActivity.kt
@@ -102,6 +102,8 @@ class GroupSettingsActivity(
DataStore.landingProxyOutbound = landingProxy
DataStore.frontProxy = if (frontProxy >= 0) 1 else 0
DataStore.landingProxy = if (landingProxy >= 0) 1 else 0
+ DataStore.groupUTLSFingerprintForTLS = utlsFingerprintForTLS
+ DataStore.groupUTLSFingerprintForReality = utlsFingerprintForReality
}
fun ProxyGroup.serialize() {
@@ -116,6 +118,8 @@ class GroupSettingsActivity(
frontProxy = if (DataStore.frontProxy == 1) DataStore.frontProxyOutbound else -1
landingProxy = if (DataStore.landingProxy == 1) DataStore.landingProxyOutbound else -1
+ utlsFingerprintForTLS = DataStore.groupUTLSFingerprintForTLS
+ utlsFingerprintForReality = DataStore.groupUTLSFingerprintForReality
if (type == GroupType.SUBSCRIPTION) {
subscription = SubscriptionBean().applyDefaultValues().apply {
type = DataStore.subscriptionType
diff --git a/app/src/main/res/xml/group_preferences.xml b/app/src/main/res/xml/group_preferences.xml
index eba545b17ac..c80b89032ac 100644
--- a/app/src/main/res/xml/group_preferences.xml
+++ b/app/src/main/res/xml/group_preferences.xml
@@ -37,6 +37,22 @@
app:key="groupLandingProxy"
app:title="@string/landing_proxy" />
+
+
+
+
diff --git a/version.properties b/version.properties
index 43342ab86b8..1905a5027bf 100644
--- a/version.properties
+++ b/version.properties
@@ -1,6 +1,6 @@
PACKAGE_NAME=com.github.dyhkwong.sagernet
VERSION_NAME=0.17.46
-VERSION_CODE=364
+VERSION_CODE=365
NAIVE_VERSION_NAME=149.0.7827.114-1
NAIVE_VERSION=275