diff --git a/app/schemas/io.nekohasekai.sagernet.database.SagerDatabase/4.json b/app/schemas/io.nekohasekai.sagernet.database.SagerDatabase/4.json
new file mode 100644
index 0000000..4f9fe26
--- /dev/null
+++ b/app/schemas/io.nekohasekai.sagernet.database.SagerDatabase/4.json
@@ -0,0 +1,360 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 4,
+ "identityHash": "cff00d0142d9e53d2ca24a6a55cd213c",
+ "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, `isSelector` INTEGER NOT NULL, `frontProxy` INTEGER NOT NULL, `landingProxy` INTEGER NOT NULL)",
+ "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",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subscription",
+ "columnName": "subscription",
+ "affinity": "BLOB",
+ "notNull": false
+ },
+ {
+ "fieldPath": "order",
+ "columnName": "order",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isSelector",
+ "columnName": "isSelector",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "frontProxy",
+ "columnName": "frontProxy",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "landingProxy",
+ "columnName": "landingProxy",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "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, `vmessBean` BLOB, `trojanBean` BLOB, `trojanGoBean` BLOB, `mieruBean` BLOB, `naiveBean` BLOB, `hysteriaBean` BLOB, `tuicBean` BLOB, `sshBean` BLOB, `wgBean` BLOB, `shadowTLSBean` BLOB, `chainBean` BLOB, `nekoBean` BLOB, `configBean` 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",
+ "notNull": false
+ },
+ {
+ "fieldPath": "socksBean",
+ "columnName": "socksBean",
+ "affinity": "BLOB",
+ "notNull": false
+ },
+ {
+ "fieldPath": "httpBean",
+ "columnName": "httpBean",
+ "affinity": "BLOB",
+ "notNull": false
+ },
+ {
+ "fieldPath": "ssBean",
+ "columnName": "ssBean",
+ "affinity": "BLOB",
+ "notNull": false
+ },
+ {
+ "fieldPath": "vmessBean",
+ "columnName": "vmessBean",
+ "affinity": "BLOB",
+ "notNull": false
+ },
+ {
+ "fieldPath": "trojanBean",
+ "columnName": "trojanBean",
+ "affinity": "BLOB",
+ "notNull": false
+ },
+ {
+ "fieldPath": "trojanGoBean",
+ "columnName": "trojanGoBean",
+ "affinity": "BLOB",
+ "notNull": false
+ },
+ {
+ "fieldPath": "mieruBean",
+ "columnName": "mieruBean",
+ "affinity": "BLOB",
+ "notNull": false
+ },
+ {
+ "fieldPath": "naiveBean",
+ "columnName": "naiveBean",
+ "affinity": "BLOB",
+ "notNull": false
+ },
+ {
+ "fieldPath": "hysteriaBean",
+ "columnName": "hysteriaBean",
+ "affinity": "BLOB",
+ "notNull": false
+ },
+ {
+ "fieldPath": "tuicBean",
+ "columnName": "tuicBean",
+ "affinity": "BLOB",
+ "notNull": false
+ },
+ {
+ "fieldPath": "sshBean",
+ "columnName": "sshBean",
+ "affinity": "BLOB",
+ "notNull": false
+ },
+ {
+ "fieldPath": "wgBean",
+ "columnName": "wgBean",
+ "affinity": "BLOB",
+ "notNull": false
+ },
+ {
+ "fieldPath": "shadowTLSBean",
+ "columnName": "shadowTLSBean",
+ "affinity": "BLOB",
+ "notNull": false
+ },
+ {
+ "fieldPath": "chainBean",
+ "columnName": "chainBean",
+ "affinity": "BLOB",
+ "notNull": false
+ },
+ {
+ "fieldPath": "nekoBean",
+ "columnName": "nekoBean",
+ "affinity": "BLOB",
+ "notNull": false
+ },
+ {
+ "fieldPath": "configBean",
+ "columnName": "configBean",
+ "affinity": "BLOB",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [
+ {
+ "name": "groupId",
+ "unique": false,
+ "columnNames": [
+ "groupId"
+ ],
+ "orders": [],
+ "createSql": "CREATE INDEX IF NOT EXISTS `groupId` ON `${TABLE_NAME}` (`groupId`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "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, `outbound` INTEGER NOT NULL, `packages` TEXT NOT NULL)",
+ "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": "outbound",
+ "columnName": "outbound",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "packages",
+ "columnName": "packages",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ }
+ ],
+ "views": [],
+ "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, 'cff00d0142d9e53d2ca24a6a55cd213c')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/schemas/io.nekohasekai.sagernet.database.SagerDatabase/5.json b/app/schemas/io.nekohasekai.sagernet.database.SagerDatabase/5.json
new file mode 100644
index 0000000..b5af39f
--- /dev/null
+++ b/app/schemas/io.nekohasekai.sagernet.database.SagerDatabase/5.json
@@ -0,0 +1,366 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 5,
+ "identityHash": "1dbf667053726c13d139a4d83c41f895",
+ "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, `isSelector` INTEGER NOT NULL, `frontProxy` INTEGER NOT NULL, `landingProxy` INTEGER NOT NULL)",
+ "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",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subscription",
+ "columnName": "subscription",
+ "affinity": "BLOB",
+ "notNull": false
+ },
+ {
+ "fieldPath": "order",
+ "columnName": "order",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isSelector",
+ "columnName": "isSelector",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "frontProxy",
+ "columnName": "frontProxy",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "landingProxy",
+ "columnName": "landingProxy",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "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, `vmessBean` BLOB, `trojanBean` BLOB, `trojanGoBean` BLOB, `mieruBean` BLOB, `naiveBean` BLOB, `hysteriaBean` BLOB, `tuicBean` BLOB, `sshBean` BLOB, `wgBean` BLOB, `shadowTLSBean` BLOB, `anyTLSBean` BLOB, `chainBean` BLOB, `nekoBean` BLOB, `configBean` 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",
+ "notNull": false
+ },
+ {
+ "fieldPath": "socksBean",
+ "columnName": "socksBean",
+ "affinity": "BLOB",
+ "notNull": false
+ },
+ {
+ "fieldPath": "httpBean",
+ "columnName": "httpBean",
+ "affinity": "BLOB",
+ "notNull": false
+ },
+ {
+ "fieldPath": "ssBean",
+ "columnName": "ssBean",
+ "affinity": "BLOB",
+ "notNull": false
+ },
+ {
+ "fieldPath": "vmessBean",
+ "columnName": "vmessBean",
+ "affinity": "BLOB",
+ "notNull": false
+ },
+ {
+ "fieldPath": "trojanBean",
+ "columnName": "trojanBean",
+ "affinity": "BLOB",
+ "notNull": false
+ },
+ {
+ "fieldPath": "trojanGoBean",
+ "columnName": "trojanGoBean",
+ "affinity": "BLOB",
+ "notNull": false
+ },
+ {
+ "fieldPath": "mieruBean",
+ "columnName": "mieruBean",
+ "affinity": "BLOB",
+ "notNull": false
+ },
+ {
+ "fieldPath": "naiveBean",
+ "columnName": "naiveBean",
+ "affinity": "BLOB",
+ "notNull": false
+ },
+ {
+ "fieldPath": "hysteriaBean",
+ "columnName": "hysteriaBean",
+ "affinity": "BLOB",
+ "notNull": false
+ },
+ {
+ "fieldPath": "tuicBean",
+ "columnName": "tuicBean",
+ "affinity": "BLOB",
+ "notNull": false
+ },
+ {
+ "fieldPath": "sshBean",
+ "columnName": "sshBean",
+ "affinity": "BLOB",
+ "notNull": false
+ },
+ {
+ "fieldPath": "wgBean",
+ "columnName": "wgBean",
+ "affinity": "BLOB",
+ "notNull": false
+ },
+ {
+ "fieldPath": "shadowTLSBean",
+ "columnName": "shadowTLSBean",
+ "affinity": "BLOB",
+ "notNull": false
+ },
+ {
+ "fieldPath": "anyTLSBean",
+ "columnName": "anyTLSBean",
+ "affinity": "BLOB",
+ "notNull": false
+ },
+ {
+ "fieldPath": "chainBean",
+ "columnName": "chainBean",
+ "affinity": "BLOB",
+ "notNull": false
+ },
+ {
+ "fieldPath": "nekoBean",
+ "columnName": "nekoBean",
+ "affinity": "BLOB",
+ "notNull": false
+ },
+ {
+ "fieldPath": "configBean",
+ "columnName": "configBean",
+ "affinity": "BLOB",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [
+ {
+ "name": "groupId",
+ "unique": false,
+ "columnNames": [
+ "groupId"
+ ],
+ "orders": [],
+ "createSql": "CREATE INDEX IF NOT EXISTS `groupId` ON `${TABLE_NAME}` (`groupId`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "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, `outbound` INTEGER NOT NULL, `packages` TEXT NOT NULL)",
+ "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": "outbound",
+ "columnName": "outbound",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "packages",
+ "columnName": "packages",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ }
+ ],
+ "views": [],
+ "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, '1dbf667053726c13d139a4d83c41f895')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 4b1fc88..c0ed87d 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -188,6 +188,9 @@
+
diff --git a/app/src/main/java/io/nekohasekai/sagernet/database/ProxyEntity.kt b/app/src/main/java/io/nekohasekai/sagernet/database/ProxyEntity.kt
index 8d2b129..3a9cc1e 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/database/ProxyEntity.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/database/ProxyEntity.kt
@@ -32,8 +32,9 @@ import io.nekohasekai.sagernet.fmt.wireguard.WireGuardBean
import io.nekohasekai.sagernet.ktx.app
import io.nekohasekai.sagernet.ktx.applyDefaultValues
import io.nekohasekai.sagernet.ui.profile.*
-import moe.matsuri.nb4a.Protocols
import moe.matsuri.nb4a.SingBoxOptions.MultiplexOptions
+import moe.matsuri.nb4a.proxy.anytls.AnyTLSBean
+import moe.matsuri.nb4a.proxy.anytls.AnyTLSSettingsActivity
import moe.matsuri.nb4a.proxy.config.ConfigBean
import moe.matsuri.nb4a.proxy.config.ConfigSettingActivity
import moe.matsuri.nb4a.proxy.neko.*
@@ -66,6 +67,7 @@ data class ProxyEntity(
var sshBean: SSHBean? = null,
var wgBean: WireGuardBean? = null,
var shadowTLSBean: ShadowTLSBean? = null,
+ var anyTLSBean: AnyTLSBean? = null,
var chainBean: ChainBean? = null,
var nekoBean: NekoBean? = null,
var configBean: ConfigBean? = null,
@@ -78,16 +80,16 @@ data class ProxyEntity(
const val TYPE_VMESS = 4
const val TYPE_TROJAN = 6
- const val TYPE_TROJAN_GO = 7
- const val TYPE_MIERU = 21
- const val TYPE_NAIVE = 9
- const val TYPE_HYSTERIA = 15
- const val TYPE_TUIC = 20
-
const val TYPE_SSH = 17
const val TYPE_WG = 18
+ const val TYPE_TROJAN_GO = 7
+ const val TYPE_NAIVE = 9
+ const val TYPE_HYSTERIA = 15
const val TYPE_SHADOWTLS = 19
+ const val TYPE_TUIC = 20
+ const val TYPE_MIERU = 21
+ const val TYPE_ANYTLS = 22
const val TYPE_CONFIG = 998
const val TYPE_NEKO = 999
@@ -173,6 +175,7 @@ data class ProxyEntity(
TYPE_WG -> wgBean = KryoConverters.wireguardDeserialize(byteArray)
TYPE_TUIC -> tuicBean = KryoConverters.tuicDeserialize(byteArray)
TYPE_SHADOWTLS -> shadowTLSBean = KryoConverters.shadowTLSDeserialize(byteArray)
+ TYPE_ANYTLS -> anyTLSBean = KryoConverters.anyTLSDeserialize(byteArray)
TYPE_CHAIN -> chainBean = KryoConverters.chainDeserialize(byteArray)
TYPE_NEKO -> nekoBean = KryoConverters.nekoDeserialize(byteArray)
TYPE_CONFIG -> configBean = KryoConverters.configDeserialize(byteArray)
@@ -193,6 +196,7 @@ data class ProxyEntity(
TYPE_WG -> "WireGuard"
TYPE_TUIC -> "TUIC"
TYPE_SHADOWTLS -> "ShadowTLS"
+ TYPE_ANYTLS -> "AnyTLS"
TYPE_CHAIN -> chainName
TYPE_NEKO -> nekoBean!!.displayType()
TYPE_CONFIG -> configBean!!.displayType()
@@ -217,6 +221,7 @@ data class ProxyEntity(
TYPE_WG -> wgBean
TYPE_TUIC -> tuicBean
TYPE_SHADOWTLS -> shadowTLSBean
+ TYPE_ANYTLS -> anyTLSBean
TYPE_CHAIN -> chainBean
TYPE_NEKO -> nekoBean
TYPE_CONFIG -> configBean
@@ -236,6 +241,7 @@ data class ProxyEntity(
is SSHBean -> false
is WireGuardBean -> false
is ShadowTLSBean -> false
+ is AnyTLSBean -> false
is NekoBean -> nekoBean!!.haveStandardLink()
is ConfigBean -> false
else -> true
@@ -352,6 +358,7 @@ data class ProxyEntity(
wgBean = null
tuicBean = null
shadowTLSBean = null
+ anyTLSBean = null
chainBean = null
configBean = null
nekoBean = null
@@ -422,6 +429,11 @@ data class ProxyEntity(
shadowTLSBean = bean
}
+ is AnyTLSBean -> {
+ type = TYPE_ANYTLS
+ anyTLSBean = bean
+ }
+
is ChainBean -> {
type = TYPE_CHAIN
chainBean = bean
@@ -458,6 +470,7 @@ data class ProxyEntity(
TYPE_WG -> WireGuardSettingsActivity::class.java
TYPE_TUIC -> TuicSettingsActivity::class.java
TYPE_SHADOWTLS -> ShadowTLSSettingsActivity::class.java
+ TYPE_ANYTLS -> AnyTLSSettingsActivity::class.java
TYPE_CHAIN -> ChainSettingsActivity::class.java
TYPE_NEKO -> NekoSettingActivity::class.java
TYPE_CONFIG -> ConfigSettingActivity::class.java
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 a235f2a..cb9cade 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/database/SagerDatabase.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/database/SagerDatabase.kt
@@ -1,5 +1,6 @@
package io.nekohasekai.sagernet.database
+import androidx.room.AutoMigration
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
@@ -15,7 +16,11 @@ import kotlinx.coroutines.launch
@Database(
entities = [ProxyGroup::class, ProxyEntity::class, RuleEntity::class],
- version = 3
+ version = 5,
+ autoMigrations = [
+ AutoMigration(from = 3, to = 4),
+ AutoMigration(from = 4, to = 5)
+ ]
)
@TypeConverters(value = [KryoConverters::class, GsonConverters::class])
@GenerateRoomMigrations
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 686bce1..dd6e816 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/fmt/ConfigBuilder.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/fmt/ConfigBuilder.kt
@@ -29,6 +29,8 @@ import io.nekohasekai.sagernet.utils.PackageCache
import moe.matsuri.nb4a.*
import moe.matsuri.nb4a.SingBoxOptions.*
import moe.matsuri.nb4a.plugin.Plugins
+import moe.matsuri.nb4a.proxy.anytls.AnyTLSBean
+import moe.matsuri.nb4a.proxy.anytls.buildSingBoxOutboundAnyTLSBean
import moe.matsuri.nb4a.proxy.config.ConfigBean
import moe.matsuri.nb4a.proxy.shadowtls.ShadowTLSBean
import moe.matsuri.nb4a.proxy.shadowtls.buildSingBoxOutboundShadowTLSBean
@@ -375,6 +377,9 @@ fun buildConfig(
is SSHBean ->
buildSingBoxOutboundSSHBean(bean).asMap()
+ is AnyTLSBean ->
+ buildSingBoxOutboundAnyTLSBean(bean).asMap()
+
else -> throw IllegalStateException("can't reach")
}
diff --git a/app/src/main/java/io/nekohasekai/sagernet/fmt/KryoConverters.java b/app/src/main/java/io/nekohasekai/sagernet/fmt/KryoConverters.java
index 627ea35..8fb9951 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/fmt/KryoConverters.java
+++ b/app/src/main/java/io/nekohasekai/sagernet/fmt/KryoConverters.java
@@ -16,6 +16,7 @@ import io.nekohasekai.sagernet.fmt.internal.ChainBean;
import io.nekohasekai.sagernet.fmt.mieru.MieruBean;
import io.nekohasekai.sagernet.fmt.naive.NaiveBean;
import io.nekohasekai.sagernet.fmt.shadowsocks.ShadowsocksBean;
+import moe.matsuri.nb4a.proxy.anytls.AnyTLSBean;
import moe.matsuri.nb4a.proxy.shadowtls.ShadowTLSBean;
import io.nekohasekai.sagernet.fmt.socks.SOCKSBean;
import io.nekohasekai.sagernet.fmt.ssh.SSHBean;
@@ -142,6 +143,13 @@ public class KryoConverters {
return deserialize(new ShadowTLSBean(), bytes);
}
+ @TypeConverter
+ public static AnyTLSBean anyTLSDeserialize(byte[] bytes) {
+ if (JavaUtil.isEmpty(bytes)) return null;
+ return deserialize(new AnyTLSBean(), bytes);
+ }
+
+
@TypeConverter
public static ChainBean chainDeserialize(byte[] bytes) {
if (JavaUtil.isEmpty(bytes)) return null;
diff --git a/app/src/main/java/io/nekohasekai/sagernet/fmt/TypeMap.kt b/app/src/main/java/io/nekohasekai/sagernet/fmt/TypeMap.kt
index 12554a1..1a91eef 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/fmt/TypeMap.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/fmt/TypeMap.kt
@@ -16,6 +16,7 @@ object TypeMap : HashMap() {
this["ssh"] = ProxyEntity.TYPE_SSH
this["wg"] = ProxyEntity.TYPE_WG
this["tuic"] = ProxyEntity.TYPE_TUIC
+ this["anytls"] = ProxyEntity.TYPE_ANYTLS
this["neko"] = ProxyEntity.TYPE_NEKO
this["config"] = ProxyEntity.TYPE_CONFIG
}
diff --git a/app/src/main/java/io/nekohasekai/sagernet/group/RawUpdater.kt b/app/src/main/java/io/nekohasekai/sagernet/group/RawUpdater.kt
index e2d0f72..4b989c4 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/group/RawUpdater.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/group/RawUpdater.kt
@@ -22,6 +22,7 @@ import io.nekohasekai.sagernet.fmt.wireguard.WireGuardBean
import io.nekohasekai.sagernet.ktx.*
import libcore.Libcore
import moe.matsuri.nb4a.Protocols
+import moe.matsuri.nb4a.proxy.anytls.AnyTLSBean
import moe.matsuri.nb4a.proxy.config.ConfigBean
import moe.matsuri.nb4a.utils.Util
import org.ini4j.Ini
@@ -510,6 +511,31 @@ object RawUpdater : GroupUpdater() {
proxies.add(bean)
}
+ "anytls" -> {
+ val bean = AnyTLSBean()
+ for (opt in proxy) {
+ if (opt.value == null) continue
+ when (opt.key.replace("_", "-")) {
+ "name" -> bean.name = opt.value.toString()
+ "server" -> bean.serverAddress = opt.value as String
+ "port" -> bean.serverPort = opt.value.toString().toInt()
+ "password" -> bean.password = opt.value.toString()
+ "client-fingerprint" -> bean.utlsFingerprint =
+ opt.value as String
+
+ "sni" -> bean.sni = opt.value.toString()
+ "skip-cert-verify" -> bean.allowInsecure =
+ opt.value.toString() == "true"
+
+ "alpn" -> {
+ val alpn = (opt.value as? (List))
+ bean.alpn = alpn?.joinToString("\n")
+ }
+ }
+ }
+ proxies.add(bean)
+ }
+
"hysteria" -> {
val bean = HysteriaBean()
bean.protocolVersion = 1
diff --git a/app/src/main/java/io/nekohasekai/sagernet/ktx/Utils.kt b/app/src/main/java/io/nekohasekai/sagernet/ktx/Utils.kt
index 01af7cb..98bbc20 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/ktx/Utils.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/ktx/Utils.kt
@@ -5,8 +5,11 @@ package io.nekohasekai.sagernet.ktx
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.annotation.SuppressLint
-import android.content.*
-import android.content.pm.PackageInfo
+import android.content.ActivityNotFoundException
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
import android.content.res.Resources
import android.os.Build
import android.system.Os
@@ -33,10 +36,17 @@ import io.nekohasekai.sagernet.database.SagerDatabase
import io.nekohasekai.sagernet.database.preference.PublicDatabase
import io.nekohasekai.sagernet.ui.MainActivity
import io.nekohasekai.sagernet.ui.ThemedActivity
-import kotlinx.coroutines.*
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.suspendCancellableCoroutine
import moe.matsuri.nb4a.utils.NGUtil
import java.io.FileDescriptor
-import java.net.*
+import java.net.HttpURLConnection
+import java.net.InetAddress
+import java.net.Socket
+import java.net.URLEncoder
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.atomic.AtomicLong
@@ -48,6 +58,7 @@ import kotlin.reflect.KMutableProperty0
import kotlin.reflect.KProperty
import kotlin.reflect.KProperty0
+fun String?.blankAsNull(): String? = if (isNullOrBlank()) null else this
inline fun Iterable.forEachTry(action: (T) -> Unit) {
var result: Exception? = null
diff --git a/app/src/main/java/io/nekohasekai/sagernet/ui/ConfigurationFragment.kt b/app/src/main/java/io/nekohasekai/sagernet/ui/ConfigurationFragment.kt
index 5b1c50f..ec53e1a 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/ui/ConfigurationFragment.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/ui/ConfigurationFragment.kt
@@ -57,6 +57,7 @@ import kotlinx.coroutines.sync.withLock
import moe.matsuri.nb4a.Protocols
import moe.matsuri.nb4a.Protocols.getProtocolColor
import moe.matsuri.nb4a.plugin.NekoPluginManager
+import moe.matsuri.nb4a.proxy.anytls.AnyTLSSettingsActivity
import moe.matsuri.nb4a.proxy.config.ConfigSettingActivity
import moe.matsuri.nb4a.proxy.neko.NekoJSInterface
import moe.matsuri.nb4a.proxy.neko.NekoSettingActivity
@@ -148,7 +149,7 @@ class ConfigurationFragment @JvmOverloads constructor(
searchView.setOnQueryTextListener(this)
searchView.maxWidth = Int.MAX_VALUE
- searchView.setOnQueryTextFocusChangeListener { _, hasFocus ->
+ searchView.setOnQueryTextFocusChangeListener { _, hasFocus ->
if (!hasFocus) {
cancelSearch(searchView)
}
@@ -390,6 +391,10 @@ class ConfigurationFragment @JvmOverloads constructor(
startActivity(Intent(requireActivity(), ShadowTLSSettingsActivity::class.java))
}
+ R.id.action_new_anytls -> {
+ startActivity(Intent(requireActivity(), AnyTLSSettingsActivity::class.java))
+ }
+
R.id.action_new_config -> {
startActivity(Intent(requireActivity(), ConfigSettingActivity::class.java))
}
@@ -1711,9 +1716,9 @@ class ConfigurationFragment @JvmOverloads constructor(
}
}
- private fun cancelSearch(searchView: SearchView) {
- searchView.onActionViewCollapsed()
- searchView.clearFocus()
- }
+ private fun cancelSearch(searchView: SearchView) {
+ searchView.onActionViewCollapsed()
+ searchView.clearFocus()
+ }
}
diff --git a/app/src/main/java/moe/matsuri/nb4a/SingBoxOptions.java b/app/src/main/java/moe/matsuri/nb4a/SingBoxOptions.java
index 0b98da0..83f1428 100644
--- a/app/src/main/java/moe/matsuri/nb4a/SingBoxOptions.java
+++ b/app/src/main/java/moe/matsuri/nb4a/SingBoxOptions.java
@@ -4335,7 +4335,6 @@ public class SingBoxOptions {
public Boolean source_ip_is_private;
- public Boolean rule_set_ipcidr_match_source;
public Boolean ip_is_private;
// Generate note: Listable
@@ -4509,4 +4508,57 @@ public class SingBoxOptions {
}
+ // sing-box Options 生成器已经坏了,以下是从 husi 抄的
+
+ public static class Outbound_AnyTLSOptions extends Outbound {
+
+ // Generate note: nested type DialerOptions
+ public String detour;
+
+ public String bind_interface;
+
+ public String inet4_bind_address;
+
+ public String inet6_bind_address;
+
+ public String protect_path;
+
+ public Integer routing_mark;
+
+ public Boolean reuse_addr;
+
+ public String connect_timeout;
+
+ public Boolean tcp_fast_open;
+
+ public Boolean tcp_multi_path;
+
+ public Boolean udp_fragment;
+
+ public String domain_strategy;
+
+ public String network_strategy;
+
+ public List network_type;
+
+ public List fallback_network_type;
+
+ public String fallback_delay;
+
+ // Generate note: nested type ServerOptions
+ public String server;
+
+ public Integer server_port;
+
+ // Generate note: nested type OutboundTLSOptionsContainer
+ public OutboundTLSOptions tls;
+
+ public String password;
+
+ public String idle_session_check_interval;
+
+ public String idle_session_timeout;
+
+ }
+
}
diff --git a/app/src/main/java/moe/matsuri/nb4a/SingBoxOptionsUtil.kt b/app/src/main/java/moe/matsuri/nb4a/SingBoxOptionsUtil.kt
index 0c2b1e9..c7059b2 100644
--- a/app/src/main/java/moe/matsuri/nb4a/SingBoxOptionsUtil.kt
+++ b/app/src/main/java/moe/matsuri/nb4a/SingBoxOptionsUtil.kt
@@ -113,7 +113,6 @@ fun SingBoxOptions.Rule_DefaultOptions.makeSingBoxRule(list: List, isIP:
if (isIP) {
if (it.startsWith("geoip:")) {
rule_set.plusAssign(it)
- rule_set_ipcidr_match_source = false
} else {
ip_cidr.plusAssign(it)
}
diff --git a/app/src/main/java/moe/matsuri/nb4a/proxy/anytls/AnyTLSBean.java b/app/src/main/java/moe/matsuri/nb4a/proxy/anytls/AnyTLSBean.java
new file mode 100644
index 0000000..43f4144
--- /dev/null
+++ b/app/src/main/java/moe/matsuri/nb4a/proxy/anytls/AnyTLSBean.java
@@ -0,0 +1,82 @@
+package moe.matsuri.nb4a.proxy.anytls;
+
+import androidx.annotation.NonNull;
+
+import com.esotericsoftware.kryo.io.ByteBufferInput;
+import com.esotericsoftware.kryo.io.ByteBufferOutput;
+
+import org.jetbrains.annotations.NotNull;
+
+import io.nekohasekai.sagernet.fmt.AbstractBean;
+import io.nekohasekai.sagernet.fmt.KryoConverters;
+
+public class AnyTLSBean extends AbstractBean {
+
+ public static final Creator CREATOR = new CREATOR() {
+ @NonNull
+ @Override
+ public AnyTLSBean newInstance() {
+ return new AnyTLSBean();
+ }
+
+ @Override
+ public AnyTLSBean[] newArray(int size) {
+ return new AnyTLSBean[size];
+ }
+ };
+ public String password;
+ public String sni;
+ public String alpn;
+ public String certificates;
+ public String utlsFingerprint;
+ public Boolean allowInsecure;
+ // In sing-box, this seemed can be used with REALITY.
+ // But even mihomo appended many options, it still not provide REALITY.
+ // https://github.com/anytls/anytls-go/blob/4636d90462fa21a510420512d7706a9acf69c7b9/docs/faq.md?plain=1#L25-L37
+
+ public String echConfig;
+
+ @Override
+ public void initializeDefaultValues() {
+ super.initializeDefaultValues();
+ if (password == null) password = "";
+ if (sni == null) sni = "";
+ if (alpn == null) alpn = "";
+ if (certificates == null) certificates = "";
+ if (utlsFingerprint == null) utlsFingerprint = "";
+ if (allowInsecure == null) allowInsecure = false;
+ if (echConfig == null) echConfig = "";
+ }
+
+ @Override
+ public void serialize(ByteBufferOutput output) {
+ output.writeInt(0);
+ super.serialize(output);
+ output.writeString(password);
+ output.writeString(sni);
+ output.writeString(alpn);
+ output.writeString(certificates);
+ output.writeString(utlsFingerprint);
+ output.writeBoolean(allowInsecure);
+ output.writeString(echConfig);
+ }
+
+ @Override
+ public void deserialize(ByteBufferInput input) {
+ int version = input.readInt();
+ super.deserialize(input);
+ password = input.readString();
+ sni = input.readString();
+ alpn = input.readString();
+ certificates = input.readString();
+ utlsFingerprint = input.readString();
+ allowInsecure = input.readBoolean();
+ echConfig = input.readString();
+ }
+
+ @NotNull
+ @Override
+ public AnyTLSBean clone() {
+ return KryoConverters.deserialize(new AnyTLSBean(), KryoConverters.serialize(this));
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/moe/matsuri/nb4a/proxy/anytls/AnyTLSFmt.kt b/app/src/main/java/moe/matsuri/nb4a/proxy/anytls/AnyTLSFmt.kt
new file mode 100644
index 0000000..06100d1
--- /dev/null
+++ b/app/src/main/java/moe/matsuri/nb4a/proxy/anytls/AnyTLSFmt.kt
@@ -0,0 +1,37 @@
+package moe.matsuri.nb4a.proxy.anytls
+
+import io.nekohasekai.sagernet.ktx.blankAsNull
+import moe.matsuri.nb4a.SingBoxOptions
+import moe.matsuri.nb4a.utils.listByLineOrComma
+
+fun buildSingBoxOutboundAnyTLSBean(bean: AnyTLSBean): SingBoxOptions.Outbound_AnyTLSOptions {
+ return SingBoxOptions.Outbound_AnyTLSOptions().apply {
+ type = "anytls"
+ server = bean.serverAddress
+ server_port = bean.serverPort
+ password = bean.password
+
+ tls = SingBoxOptions.OutboundTLSOptions().apply {
+ enabled = true
+ server_name = bean.sni.blankAsNull()
+ if (bean.allowInsecure) insecure = true
+ alpn = bean.alpn.blankAsNull()?.listByLineOrComma()
+ bean.certificates.blankAsNull()?.let {
+ certificate = it
+ }
+ bean.utlsFingerprint.blankAsNull()?.let {
+ utls = SingBoxOptions.OutboundUTLSOptions().apply {
+ enabled = true
+ fingerprint = it
+ }
+ }
+ bean.echConfig.blankAsNull()?.let {
+ // In new version, some complex options will be deprecated, so we just do this.
+ ech = SingBoxOptions.OutboundECHOptions().apply {
+ enabled = true
+ config = listOf(it)
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/moe/matsuri/nb4a/proxy/anytls/AnyTLSSettingsActivity.kt b/app/src/main/java/moe/matsuri/nb4a/proxy/anytls/AnyTLSSettingsActivity.kt
new file mode 100644
index 0000000..13d256d
--- /dev/null
+++ b/app/src/main/java/moe/matsuri/nb4a/proxy/anytls/AnyTLSSettingsActivity.kt
@@ -0,0 +1,51 @@
+package moe.matsuri.nb4a.proxy.anytls
+
+import android.os.Bundle
+import androidx.preference.EditTextPreference
+import androidx.preference.PreferenceFragmentCompat
+import io.nekohasekai.sagernet.Key
+import io.nekohasekai.sagernet.R
+import io.nekohasekai.sagernet.database.preference.EditTextPreferenceModifiers
+import io.nekohasekai.sagernet.ktx.applyDefaultValues
+import io.nekohasekai.sagernet.ui.profile.ProfileSettingsActivity
+import moe.matsuri.nb4a.proxy.PreferenceBinding
+import moe.matsuri.nb4a.proxy.PreferenceBindingManager
+import moe.matsuri.nb4a.proxy.Type
+
+class AnyTLSSettingsActivity : ProfileSettingsActivity() {
+ override fun createEntity() = AnyTLSBean().applyDefaultValues()
+
+ private val pbm = PreferenceBindingManager()
+ private val name = pbm.add(PreferenceBinding(Type.Text, "name"))
+ private val serverAddress = pbm.add(PreferenceBinding(Type.Text, "serverAddress"))
+ private val serverPort = pbm.add(PreferenceBinding(Type.TextToInt, "serverPort"))
+ private val password = pbm.add(PreferenceBinding(Type.Text, "password"))
+ private val sni = pbm.add(PreferenceBinding(Type.Text, "sni"))
+ private val alpn = pbm.add(PreferenceBinding(Type.Text, "alpn"))
+ private val certificates = pbm.add(PreferenceBinding(Type.Text, "certificates"))
+ private val allowInsecure = pbm.add(PreferenceBinding(Type.Bool, "allowInsecure"))
+ private val utlsFingerprint = pbm.add(PreferenceBinding(Type.Text, "utlsFingerprint"))
+
+ override fun AnyTLSBean.init() {
+ pbm.writeToCacheAll(this)
+
+ }
+
+ override fun AnyTLSBean.serialize() {
+ pbm.fromCacheAll(this)
+ }
+
+ override fun PreferenceFragmentCompat.createPreferences(
+ savedInstanceState: Bundle?,
+ rootKey: String?
+ ) {
+ addPreferencesFromResource(R.xml.anytls_preferences)
+
+ findPreference(Key.SERVER_PORT)!!.apply {
+ setOnBindEditTextListener(EditTextPreferenceModifiers.Port)
+ }
+ findPreference("password")!!.apply {
+ summaryProvider = PasswordSummaryProvider
+ }
+ }
+}
diff --git a/app/src/main/res/menu/add_profile_menu.xml b/app/src/main/res/menu/add_profile_menu.xml
index 129d320..9ab6929 100644
--- a/app/src/main/res/menu/add_profile_menu.xml
+++ b/app/src/main/res/menu/add_profile_menu.xml
@@ -57,15 +57,18 @@
+
+
-
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 196858c..920679f 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -216,7 +216,7 @@
Trojan Go
Mieru
Naïve
- Ping Tunnel
+ AnyTLS
Hysteria
SSH
WireGuard
diff --git a/app/src/main/res/xml/anytls_preferences.xml b/app/src/main/res/xml/anytls_preferences.xml
new file mode 100644
index 0000000..d77f793
--- /dev/null
+++ b/app/src/main/res/xml/anytls_preferences.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/global_preferences.xml b/app/src/main/res/xml/global_preferences.xml
index 463e92a..cc51866 100644
--- a/app/src/main/res/xml/global_preferences.xml
+++ b/app/src/main/res/xml/global_preferences.xml
@@ -2,10 +2,6 @@
-
+
@@ -234,14 +234,14 @@
app:title="@string/app_tls_version"
app:useSimpleSummaryProvider="true" />
+ app:icon="@drawable/ic_action_lock_open"
+ app:key="globalAllowInsecure"
+ app:title="@string/global_allow_insecure" />
+ app:key="showBottomBar"
+ app:title="@string/show_bottom_bar" />