mirror of
https://github.com/MatsuriDayo/NekoBoxForAndroid.git
synced 2025-12-19 22:50:05 +08:00
tuic v5 support
This commit is contained in:
parent
34a3012772
commit
4b1800d782
@ -78,14 +78,11 @@ object Key {
|
|||||||
const val SERVER_USERNAME = "serverUsername"
|
const val SERVER_USERNAME = "serverUsername"
|
||||||
const val SERVER_PASSWORD = "serverPassword"
|
const val SERVER_PASSWORD = "serverPassword"
|
||||||
const val SERVER_METHOD = "serverMethod"
|
const val SERVER_METHOD = "serverMethod"
|
||||||
const val SERVER_PLUGIN = "serverPlugin"
|
|
||||||
const val SERVER_PLUGIN_CONFIGURE = "serverPluginConfigure"
|
|
||||||
const val SERVER_PASSWORD1 = "serverPassword1"
|
const val SERVER_PASSWORD1 = "serverPassword1"
|
||||||
|
|
||||||
const val SERVER_PROTOCOL = "serverProtocol"
|
const val SERVER_PROTOCOL = "serverProtocol"
|
||||||
const val SERVER_OBFS = "serverObfs"
|
const val SERVER_OBFS = "serverObfs"
|
||||||
|
|
||||||
const val SERVER_SECURITY = "serverSecurity"
|
|
||||||
const val SERVER_NETWORK = "serverNetwork"
|
const val SERVER_NETWORK = "serverNetwork"
|
||||||
const val SERVER_HOST = "serverHost"
|
const val SERVER_HOST = "serverHost"
|
||||||
const val SERVER_PATH = "serverPath"
|
const val SERVER_PATH = "serverPath"
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import io.nekohasekai.sagernet.fmt.trojan_go.TrojanGoBean
|
|||||||
import io.nekohasekai.sagernet.fmt.trojan_go.buildTrojanGoConfig
|
import io.nekohasekai.sagernet.fmt.trojan_go.buildTrojanGoConfig
|
||||||
import io.nekohasekai.sagernet.fmt.tuic.TuicBean
|
import io.nekohasekai.sagernet.fmt.tuic.TuicBean
|
||||||
import io.nekohasekai.sagernet.fmt.tuic.buildTuicConfig
|
import io.nekohasekai.sagernet.fmt.tuic.buildTuicConfig
|
||||||
|
import io.nekohasekai.sagernet.fmt.tuic.pluginId
|
||||||
import io.nekohasekai.sagernet.ktx.*
|
import io.nekohasekai.sagernet.ktx.*
|
||||||
import io.nekohasekai.sagernet.plugin.PluginManager
|
import io.nekohasekai.sagernet.plugin.PluginManager
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
@ -86,7 +87,7 @@ abstract class BoxInstance(
|
|||||||
}
|
}
|
||||||
|
|
||||||
is TuicBean -> {
|
is TuicBean -> {
|
||||||
initPlugin("tuic-plugin")
|
initPlugin(bean.pluginId())
|
||||||
pluginConfigs[port] = profile.type to bean.buildTuicConfig(port) {
|
pluginConfigs[port] = profile.type to bean.buildTuicConfig(port) {
|
||||||
File(
|
File(
|
||||||
app.noBackupFilesDir,
|
app.noBackupFilesDir,
|
||||||
@ -252,7 +253,7 @@ abstract class BoxInstance(
|
|||||||
cacheFiles.add(configFile)
|
cacheFiles.add(configFile)
|
||||||
|
|
||||||
val commands = mutableListOf(
|
val commands = mutableListOf(
|
||||||
initPlugin("tuic-plugin").path,
|
initPlugin(bean.pluginId()).path,
|
||||||
"-c",
|
"-c",
|
||||||
configFile.absolutePath,
|
configFile.absolutePath,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import io.nekohasekai.sagernet.fmt.socks.buildSingBoxOutboundSocksBean
|
|||||||
import io.nekohasekai.sagernet.fmt.ssh.SSHBean
|
import io.nekohasekai.sagernet.fmt.ssh.SSHBean
|
||||||
import io.nekohasekai.sagernet.fmt.ssh.buildSingBoxOutboundSSHBean
|
import io.nekohasekai.sagernet.fmt.ssh.buildSingBoxOutboundSSHBean
|
||||||
import io.nekohasekai.sagernet.fmt.tuic.TuicBean
|
import io.nekohasekai.sagernet.fmt.tuic.TuicBean
|
||||||
|
import io.nekohasekai.sagernet.fmt.tuic.pluginId
|
||||||
import io.nekohasekai.sagernet.fmt.v2ray.StandardV2RayBean
|
import io.nekohasekai.sagernet.fmt.v2ray.StandardV2RayBean
|
||||||
import io.nekohasekai.sagernet.fmt.v2ray.buildSingBoxOutboundStandardV2RayBean
|
import io.nekohasekai.sagernet.fmt.v2ray.buildSingBoxOutboundStandardV2RayBean
|
||||||
import io.nekohasekai.sagernet.fmt.wireguard.WireGuardBean
|
import io.nekohasekai.sagernet.fmt.wireguard.WireGuardBean
|
||||||
@ -35,6 +36,7 @@ import moe.matsuri.nb4a.proxy.config.ConfigBean
|
|||||||
import moe.matsuri.nb4a.proxy.shadowtls.ShadowTLSBean
|
import moe.matsuri.nb4a.proxy.shadowtls.ShadowTLSBean
|
||||||
import moe.matsuri.nb4a.proxy.shadowtls.buildSingBoxOutboundShadowTLSBean
|
import moe.matsuri.nb4a.proxy.shadowtls.buildSingBoxOutboundShadowTLSBean
|
||||||
import moe.matsuri.nb4a.utils.JavaUtil.gson
|
import moe.matsuri.nb4a.utils.JavaUtil.gson
|
||||||
|
import moe.matsuri.nb4a.utils.Util
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||||
|
|
||||||
const val TAG_MIXED = "mixed-in"
|
const val TAG_MIXED = "mixed-in"
|
||||||
@ -63,20 +65,6 @@ class ConfigBuildResult(
|
|||||||
data class IndexEntity(var chain: LinkedHashMap<Int, ProxyEntity>)
|
data class IndexEntity(var chain: LinkedHashMap<Int, ProxyEntity>)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun mergeJSON(j: String, to: MutableMap<String, Any>) {
|
|
||||||
if (j.isBlank()) return
|
|
||||||
val m = gson.fromJson(j, to.javaClass)
|
|
||||||
m.forEach { (k, v) ->
|
|
||||||
if (v is Map<*, *> && to[k] is Map<*, *>) {
|
|
||||||
val currentMap = (to[k] as Map<*, *>).toMutableMap()
|
|
||||||
currentMap += v
|
|
||||||
to[k] = currentMap
|
|
||||||
} else {
|
|
||||||
to[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun buildConfig(
|
fun buildConfig(
|
||||||
proxy: ProxyEntity, forTest: Boolean = false, forExport: Boolean = false
|
proxy: ProxyEntity, forTest: Boolean = false, forExport: Boolean = false
|
||||||
): ConfigBuildResult {
|
): ConfigBuildResult {
|
||||||
@ -439,7 +427,7 @@ fun buildConfig(
|
|||||||
|
|
||||||
// custom JSON merge
|
// custom JSON merge
|
||||||
if (bean.customOutboundJson.isNotBlank()) {
|
if (bean.customOutboundJson.isNotBlank()) {
|
||||||
mergeJSON(bean.customOutboundJson, currentOutbound)
|
Util.mergeJSON(bean.customOutboundJson, currentOutbound)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -458,18 +446,19 @@ fun buildConfig(
|
|||||||
|
|
||||||
// External proxy need a dokodemo-door inbound to forward the traffic
|
// External proxy need a dokodemo-door inbound to forward the traffic
|
||||||
// For external proxy software, their traffic must goes to v2ray-core to use protected fd.
|
// For external proxy software, their traffic must goes to v2ray-core to use protected fd.
|
||||||
|
bean.finalPort = bean.serverPort
|
||||||
if (bean.canMapping() && proxyEntity.needExternal()) {
|
if (bean.canMapping() && proxyEntity.needExternal()) {
|
||||||
// With ss protect, don't use mapping
|
// With ss protect, don't use mapping
|
||||||
var needExternal = true
|
var needExternal = true
|
||||||
if (index == profileList.lastIndex) {
|
if (index == profileList.lastIndex) {
|
||||||
val pluginId = when (bean) {
|
val pluginId = when (bean) {
|
||||||
is HysteriaBean -> "hysteria-plugin"
|
is HysteriaBean -> "hysteria-plugin"
|
||||||
is TuicBean -> "tuic-plugin"
|
is TuicBean -> bean.pluginId()
|
||||||
else -> ""
|
else -> ""
|
||||||
}
|
}
|
||||||
if (Plugins.isUsingMatsuriExe(pluginId)) {
|
if (Plugins.isUsingMatsuriExe(pluginId)) {
|
||||||
needExternal = false
|
needExternal = false
|
||||||
} else if (pluginId.isNotBlank()) {
|
} else if (Plugins.getPlugin(pluginId) != null) {
|
||||||
throw Exception("You are using an unsupported $pluginId, please download the correct plugin.")
|
throw Exception("You are using an unsupported $pluginId, please download the correct plugin.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -593,9 +582,12 @@ fun buildConfig(
|
|||||||
if (uidList.isNotEmpty()) user_id = uidList
|
if (uidList.isNotEmpty()) user_id = uidList
|
||||||
domainList?.let { makeSingBoxRule(it) }
|
domainList?.let { makeSingBoxRule(it) }
|
||||||
}
|
}
|
||||||
if (rule.outbound == -1L) {
|
when (rule.outbound) {
|
||||||
|
-1L -> {
|
||||||
userDNSRuleList += dnsRuleObj.apply { server = "dns-direct" }
|
userDNSRuleList += dnsRuleObj.apply { server = "dns-direct" }
|
||||||
} else if (rule.outbound == 0L) {
|
}
|
||||||
|
|
||||||
|
0L -> {
|
||||||
if (useFakeDns) userDNSRuleList += dnsRuleObj.apply {
|
if (useFakeDns) userDNSRuleList += dnsRuleObj.apply {
|
||||||
server = "dns-fake"
|
server = "dns-fake"
|
||||||
inbound = listOf("tun-in")
|
inbound = listOf("tun-in")
|
||||||
@ -604,9 +596,12 @@ fun buildConfig(
|
|||||||
server = "dns-remote"
|
server = "dns-remote"
|
||||||
inbound = null
|
inbound = null
|
||||||
}
|
}
|
||||||
} else if (rule.outbound == -2L) {
|
}
|
||||||
|
|
||||||
|
-2L -> {
|
||||||
userDNSRuleList += dnsRuleObj.apply { server = "dns-block" }
|
userDNSRuleList += dnsRuleObj.apply { server = "dns-block" }
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
outbound = when (val outId = rule.outbound) {
|
outbound = when (val outId = rule.outbound) {
|
||||||
0L -> TAG_PROXY
|
0L -> TAG_PROXY
|
||||||
@ -786,7 +781,7 @@ fun buildConfig(
|
|||||||
}.let {
|
}.let {
|
||||||
ConfigBuildResult(
|
ConfigBuildResult(
|
||||||
gson.toJson(it.asMap().apply {
|
gson.toJson(it.asMap().apply {
|
||||||
mergeJSON(optionsToMerge, this)
|
Util.mergeJSON(optionsToMerge, this)
|
||||||
}),
|
}),
|
||||||
externalIndexMap,
|
externalIndexMap,
|
||||||
proxy.id,
|
proxy.id,
|
||||||
|
|||||||
@ -22,7 +22,8 @@ enum class PluginEntry(
|
|||||||
Hysteria(
|
Hysteria(
|
||||||
"hysteria-plugin",
|
"hysteria-plugin",
|
||||||
SagerNet.application.getString(R.string.action_hysteria),
|
SagerNet.application.getString(R.string.action_hysteria),
|
||||||
"moe.matsuri.exe.hysteria", DownloadSource(
|
"moe.matsuri.exe.hysteria",
|
||||||
|
DownloadSource(
|
||||||
playStore = false,
|
playStore = false,
|
||||||
fdroid = false,
|
fdroid = false,
|
||||||
downloadLink = "https://github.com/MatsuriDayo/plugins/releases?q=Hysteria"
|
downloadLink = "https://github.com/MatsuriDayo/plugins/releases?q=Hysteria"
|
||||||
@ -30,8 +31,19 @@ enum class PluginEntry(
|
|||||||
),
|
),
|
||||||
TUIC(
|
TUIC(
|
||||||
"tuic-plugin",
|
"tuic-plugin",
|
||||||
SagerNet.application.getString(R.string.action_tuic),
|
"TUIC(v4)",
|
||||||
"moe.matsuri.exe.tuic", DownloadSource(
|
"moe.matsuri.exe.tuic",
|
||||||
|
DownloadSource(
|
||||||
|
playStore = false,
|
||||||
|
fdroid = false,
|
||||||
|
downloadLink = "https://github.com/MatsuriDayo/plugins/releases?q=tuic"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
TUIC5(
|
||||||
|
"tuic-v5-plugin",
|
||||||
|
"TUIC(v5)",
|
||||||
|
"moe.matsuri.exe.tuic5",
|
||||||
|
DownloadSource(
|
||||||
playStore = false,
|
playStore = false,
|
||||||
fdroid = false,
|
fdroid = false,
|
||||||
downloadLink = "https://github.com/MatsuriDayo/plugins/releases?q=tuic"
|
downloadLink = "https://github.com/MatsuriDayo/plugins/releases?q=tuic"
|
||||||
|
|||||||
@ -24,6 +24,12 @@ public class TuicBean extends AbstractBean {
|
|||||||
public Boolean fastConnect;
|
public Boolean fastConnect;
|
||||||
public Boolean allowInsecure;
|
public Boolean allowInsecure;
|
||||||
|
|
||||||
|
// TUIC v5
|
||||||
|
|
||||||
|
public String customJSON;
|
||||||
|
public Integer protocolVersion;
|
||||||
|
public String uuid;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initializeDefaultValues() {
|
public void initializeDefaultValues() {
|
||||||
super.initializeDefaultValues();
|
super.initializeDefaultValues();
|
||||||
@ -38,11 +44,14 @@ public class TuicBean extends AbstractBean {
|
|||||||
if (sni == null) sni = "";
|
if (sni == null) sni = "";
|
||||||
if (fastConnect == null) fastConnect = false;
|
if (fastConnect == null) fastConnect = false;
|
||||||
if (allowInsecure == null) allowInsecure = false;
|
if (allowInsecure == null) allowInsecure = false;
|
||||||
|
if (customJSON == null) customJSON = "";
|
||||||
|
if (protocolVersion == null) protocolVersion = 4;
|
||||||
|
if (uuid == null) uuid = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void serialize(ByteBufferOutput output) {
|
public void serialize(ByteBufferOutput output) {
|
||||||
output.writeInt(1);
|
output.writeInt(2);
|
||||||
super.serialize(output);
|
super.serialize(output);
|
||||||
output.writeString(token);
|
output.writeString(token);
|
||||||
output.writeString(caText);
|
output.writeString(caText);
|
||||||
@ -55,6 +64,9 @@ public class TuicBean extends AbstractBean {
|
|||||||
output.writeString(sni);
|
output.writeString(sni);
|
||||||
output.writeBoolean(fastConnect);
|
output.writeBoolean(fastConnect);
|
||||||
output.writeBoolean(allowInsecure);
|
output.writeBoolean(allowInsecure);
|
||||||
|
output.writeString(customJSON);
|
||||||
|
output.writeInt(protocolVersion);
|
||||||
|
output.writeString(uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -74,6 +86,11 @@ public class TuicBean extends AbstractBean {
|
|||||||
fastConnect = input.readBoolean();
|
fastConnect = input.readBoolean();
|
||||||
allowInsecure = input.readBoolean();
|
allowInsecure = input.readBoolean();
|
||||||
}
|
}
|
||||||
|
if (version >= 2) {
|
||||||
|
customJSON = input.readString();
|
||||||
|
protocolVersion = input.readInt();
|
||||||
|
uuid = input.readString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -1,39 +1,82 @@
|
|||||||
package io.nekohasekai.sagernet.fmt.tuic
|
package io.nekohasekai.sagernet.fmt.tuic
|
||||||
|
|
||||||
import io.nekohasekai.sagernet.database.DataStore
|
|
||||||
import io.nekohasekai.sagernet.fmt.LOCALHOST
|
import io.nekohasekai.sagernet.fmt.LOCALHOST
|
||||||
import io.nekohasekai.sagernet.ktx.isIpAddress
|
import io.nekohasekai.sagernet.ktx.isIpAddress
|
||||||
import io.nekohasekai.sagernet.ktx.toStringPretty
|
import io.nekohasekai.sagernet.ktx.wrapIPV6Host
|
||||||
import kotlinx.coroutines.Dispatchers
|
import moe.matsuri.nb4a.utils.JavaUtil
|
||||||
import kotlinx.coroutines.runBlocking
|
import moe.matsuri.nb4a.utils.Util
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import moe.matsuri.nb4a.plugin.Plugins
|
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.InetAddress
|
|
||||||
|
fun TuicBean.pluginId(): String {
|
||||||
|
return when (protocolVersion) {
|
||||||
|
5 -> "tuic-v5-plugin"
|
||||||
|
else -> "tuic-plugin"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun TuicBean.buildTuicConfig(port: Int, cacheFile: (() -> File)?): String {
|
fun TuicBean.buildTuicConfig(port: Int, cacheFile: (() -> File)?): String {
|
||||||
if (Plugins.isUsingMatsuriExe("tuic-plugin")) {
|
val config = when (protocolVersion) {
|
||||||
if (!serverAddress.isIpAddress()) {
|
5 -> buildTuicConfigV5(port, cacheFile)
|
||||||
runBlocking {
|
else -> buildTuicConfigV4(port, cacheFile)
|
||||||
finalAddress = withContext(Dispatchers.IO) {
|
}.toString()
|
||||||
InetAddress.getAllByName(serverAddress)
|
var gsonMap = mutableMapOf<String, Any>()
|
||||||
}?.firstOrNull()?.hostAddress ?: "127.0.0.1"
|
gsonMap = JavaUtil.gson.fromJson(config, gsonMap.javaClass)
|
||||||
// TODO network on main thread, tuic don't support "sni"
|
Util.mergeJSON(customJSON, gsonMap)
|
||||||
}
|
return JavaUtil.gson.toJson(gsonMap)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
fun TuicBean.buildTuicConfigV5(port: Int, cacheFile: (() -> File)?): JSONObject {
|
||||||
return JSONObject().apply {
|
return JSONObject().apply {
|
||||||
put("relay", JSONObject().apply {
|
put("relay", JSONObject().apply {
|
||||||
if (sni.isNotBlank()) {
|
if (sni.isNotBlank() && !disableSNI) {
|
||||||
put("server", sni)
|
put("server", "$sni:$finalPort")
|
||||||
|
if (finalAddress.isIpAddress()) {
|
||||||
put("ip", finalAddress)
|
put("ip", finalAddress)
|
||||||
} else if (serverAddress.isIpAddress()) {
|
|
||||||
put("server", finalAddress)
|
|
||||||
} else {
|
} else {
|
||||||
put("server", serverAddress)
|
throw Exception("TUIC must use IP address when you need spoof SNI.")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
put("server", serverAddress.wrapIPV6Host() + ":" + finalPort)
|
||||||
|
}
|
||||||
|
|
||||||
|
put("uuid", uuid)
|
||||||
|
put("password", token)
|
||||||
|
|
||||||
|
if (caText.isNotBlank() && cacheFile != null) {
|
||||||
|
val caFile = cacheFile()
|
||||||
|
caFile.writeText(caText)
|
||||||
|
put("certificates", JSONArray(listOf(caFile.absolutePath)))
|
||||||
|
}
|
||||||
|
|
||||||
|
put("udp_relay_mode", udpRelayMode)
|
||||||
|
if (alpn.isNotBlank()) {
|
||||||
|
put("alpn", JSONArray(alpn.split("\n")))
|
||||||
|
}
|
||||||
|
put("congestion_control", congestionController)
|
||||||
|
put("disable_sni", disableSNI)
|
||||||
|
put("zero_rtt_handshake", disableSNI)
|
||||||
|
})
|
||||||
|
put("local", JSONObject().apply {
|
||||||
|
put("server", "127.0.0.1:$port")
|
||||||
|
})
|
||||||
|
put("log_level", "debug")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun TuicBean.buildTuicConfigV4(port: Int, cacheFile: (() -> File)?): JSONObject {
|
||||||
|
return JSONObject().apply {
|
||||||
|
put("relay", JSONObject().apply {
|
||||||
|
if (sni.isNotBlank() && !disableSNI) {
|
||||||
|
put("server", sni)
|
||||||
|
if (finalAddress.isIpAddress()) {
|
||||||
put("ip", finalAddress)
|
put("ip", finalAddress)
|
||||||
|
} else {
|
||||||
|
throw Exception("TUIC must use IP address when you need spoof SNI.")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
put("server", finalAddress)
|
||||||
}
|
}
|
||||||
put("port", finalPort)
|
put("port", finalPort)
|
||||||
put("token", token)
|
put("token", token)
|
||||||
@ -59,6 +102,6 @@ fun TuicBean.buildTuicConfig(port: Int, cacheFile: (() -> File)?): String {
|
|||||||
put("ip", LOCALHOST)
|
put("ip", LOCALHOST)
|
||||||
put("port", port)
|
put("port", port)
|
||||||
})
|
})
|
||||||
put("log_level", if (DataStore.logLevel > 0) "debug" else "info")
|
put("log_level", "debug")
|
||||||
}.toStringPretty()
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,8 @@ import io.nekohasekai.sagernet.R
|
|||||||
import io.nekohasekai.sagernet.database.DataStore
|
import io.nekohasekai.sagernet.database.DataStore
|
||||||
import io.nekohasekai.sagernet.fmt.tuic.TuicBean
|
import io.nekohasekai.sagernet.fmt.tuic.TuicBean
|
||||||
import io.nekohasekai.sagernet.ktx.applyDefaultValues
|
import io.nekohasekai.sagernet.ktx.applyDefaultValues
|
||||||
|
import moe.matsuri.nb4a.ui.EditConfigPreference
|
||||||
|
import moe.matsuri.nb4a.ui.SimpleMenuPreference
|
||||||
|
|
||||||
class TuicSettingsActivity : ProfileSettingsActivity<TuicBean>() {
|
class TuicSettingsActivity : ProfileSettingsActivity<TuicBean>() {
|
||||||
|
|
||||||
@ -27,8 +29,13 @@ class TuicSettingsActivity : ProfileSettingsActivity<TuicBean>() {
|
|||||||
DataStore.serverSNI = sni
|
DataStore.serverSNI = sni
|
||||||
DataStore.serverReduceRTT = reduceRTT
|
DataStore.serverReduceRTT = reduceRTT
|
||||||
DataStore.serverMTU = mtu
|
DataStore.serverMTU = mtu
|
||||||
|
//
|
||||||
DataStore.serverFastConnect = fastConnect
|
DataStore.serverFastConnect = fastConnect
|
||||||
DataStore.serverAllowInsecure = allowInsecure
|
DataStore.serverAllowInsecure = allowInsecure
|
||||||
|
//
|
||||||
|
DataStore.serverConfig = customJSON
|
||||||
|
DataStore.serverProtocolVersion = protocolVersion
|
||||||
|
DataStore.serverUsername = uuid
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun TuicBean.serialize() {
|
override fun TuicBean.serialize() {
|
||||||
@ -44,16 +51,48 @@ class TuicSettingsActivity : ProfileSettingsActivity<TuicBean>() {
|
|||||||
sni = DataStore.serverSNI
|
sni = DataStore.serverSNI
|
||||||
reduceRTT = DataStore.serverReduceRTT
|
reduceRTT = DataStore.serverReduceRTT
|
||||||
mtu = DataStore.serverMTU
|
mtu = DataStore.serverMTU
|
||||||
|
//
|
||||||
fastConnect = DataStore.serverFastConnect
|
fastConnect = DataStore.serverFastConnect
|
||||||
allowInsecure = DataStore.serverAllowInsecure
|
allowInsecure = DataStore.serverAllowInsecure
|
||||||
|
//
|
||||||
|
customJSON = DataStore.serverConfig
|
||||||
|
protocolVersion = DataStore.serverProtocolVersion
|
||||||
|
uuid = DataStore.serverUsername
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private lateinit var editConfigPreference: EditConfigPreference
|
||||||
|
|
||||||
override fun PreferenceFragmentCompat.createPreferences(
|
override fun PreferenceFragmentCompat.createPreferences(
|
||||||
savedInstanceState: Bundle?,
|
savedInstanceState: Bundle?,
|
||||||
rootKey: String?,
|
rootKey: String?,
|
||||||
) {
|
) {
|
||||||
addPreferencesFromResource(R.xml.tuic_preferences)
|
addPreferencesFromResource(R.xml.tuic_preferences)
|
||||||
|
|
||||||
|
editConfigPreference = findPreference(Key.SERVER_CONFIG)!!
|
||||||
|
|
||||||
|
val uuid = findPreference<EditTextPreference>(Key.SERVER_USERNAME)!!
|
||||||
|
val mtu = findPreference<EditTextPreference>(Key.SERVER_MTU)!!
|
||||||
|
val fastConnect = findPreference<SwitchPreference>(Key.SERVER_FAST_CONNECT)!!
|
||||||
|
val allowInsecure = findPreference<SwitchPreference>(Key.SERVER_ALLOW_INSECURE)!!
|
||||||
|
fun updateVersion(v: Int) {
|
||||||
|
if (v == 5) {
|
||||||
|
uuid.isVisible = true
|
||||||
|
mtu.isVisible = false
|
||||||
|
fastConnect.isVisible = false
|
||||||
|
allowInsecure.isVisible = false
|
||||||
|
} else {
|
||||||
|
uuid.isVisible = false
|
||||||
|
mtu.isVisible = true
|
||||||
|
fastConnect.isVisible = true
|
||||||
|
allowInsecure.isVisible = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
findPreference<SimpleMenuPreference>(Key.SERVER_PROTOCOL)!!.setOnPreferenceChangeListener { _, newValue ->
|
||||||
|
updateVersion(newValue.toString().toIntOrNull() ?: 4)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
updateVersion(DataStore.serverProtocolVersion)
|
||||||
|
|
||||||
val disableSNI = findPreference<SwitchPreference>(Key.SERVER_DISABLE_SNI)!!
|
val disableSNI = findPreference<SwitchPreference>(Key.SERVER_DISABLE_SNI)!!
|
||||||
val sni = findPreference<EditTextPreference>(Key.SERVER_SNI)!!
|
val sni = findPreference<EditTextPreference>(Key.SERVER_SNI)!!
|
||||||
sni.isEnabled = !disableSNI.isChecked
|
sni.isEnabled = !disableSNI.isChecked
|
||||||
@ -67,4 +106,12 @@ class TuicSettingsActivity : ProfileSettingsActivity<TuicBean>() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
|
||||||
|
if (::editConfigPreference.isInitialized) {
|
||||||
|
editConfigPreference.notifyChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
package moe.matsuri.nb4a.utils
|
package moe.matsuri.nb4a.utils
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import androidx.appcompat.content.res.AppCompatResources
|
import androidx.appcompat.content.res.AppCompatResources
|
||||||
@ -34,6 +35,7 @@ fun File.recreate(dir: Boolean) {
|
|||||||
|
|
||||||
// Context utils
|
// Context utils
|
||||||
|
|
||||||
|
@SuppressLint("DiscouragedApi")
|
||||||
fun Context.getDrawableByName(name: String?): Drawable? {
|
fun Context.getDrawableByName(name: String?): Drawable? {
|
||||||
val resourceId: Int = resources.getIdentifier(name, "drawable", packageName)
|
val resourceId: Int = resources.getIdentifier(name, "drawable", packageName)
|
||||||
return AppCompatResources.getDrawable(this, resourceId)
|
return AppCompatResources.getDrawable(this, resourceId)
|
||||||
|
|||||||
@ -113,6 +113,21 @@ object Util {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun mergeJSON(j: String, to: MutableMap<String, Any>) {
|
||||||
|
if (j.isBlank()) return
|
||||||
|
val m = JavaUtil.gson.fromJson(j, to.javaClass)
|
||||||
|
m.forEach { (k, v) ->
|
||||||
|
if (v is Map<*, *> && to[k] is Map<*, *>) {
|
||||||
|
val currentMap = (to[k] as Map<*, *>).toMutableMap()
|
||||||
|
currentMap += v
|
||||||
|
to[k] = currentMap
|
||||||
|
} else {
|
||||||
|
to[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Format Time
|
// Format Time
|
||||||
|
|
||||||
@SuppressLint("SimpleDateFormat")
|
@SuppressLint("SimpleDateFormat")
|
||||||
|
|||||||
@ -386,6 +386,16 @@
|
|||||||
<item>BASE64</item>
|
<item>BASE64</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="tuic_version">
|
||||||
|
<item>v4</item>
|
||||||
|
<item>v5</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="tuic_version_value">
|
||||||
|
<item>4</item>
|
||||||
|
<item>5</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
<string-array name="socks_versions">
|
<string-array name="socks_versions">
|
||||||
<item>SOCKS4</item>
|
<item>SOCKS4</item>
|
||||||
<item>SOCKS4A</item>
|
<item>SOCKS4A</item>
|
||||||
|
|||||||
@ -6,6 +6,15 @@
|
|||||||
app:title="@string/profile_name"
|
app:title="@string/profile_name"
|
||||||
app:useSimpleSummaryProvider="true" />
|
app:useSimpleSummaryProvider="true" />
|
||||||
|
|
||||||
|
<moe.matsuri.nb4a.ui.SimpleMenuPreference
|
||||||
|
app:defaultValue="4"
|
||||||
|
app:entries="@array/tuic_version"
|
||||||
|
app:entryValues="@array/tuic_version_value"
|
||||||
|
app:icon="@drawable/ic_baseline_nfc_24"
|
||||||
|
app:key="serverProtocol"
|
||||||
|
app:title="@string/app_version"
|
||||||
|
app:useSimpleSummaryProvider="true" />
|
||||||
|
|
||||||
<PreferenceCategory app:title="@string/proxy_cat">
|
<PreferenceCategory app:title="@string/proxy_cat">
|
||||||
|
|
||||||
<EditTextPreference
|
<EditTextPreference
|
||||||
@ -18,11 +27,16 @@
|
|||||||
app:key="serverPort"
|
app:key="serverPort"
|
||||||
app:title="@string/server_port"
|
app:title="@string/server_port"
|
||||||
app:useSimpleSummaryProvider="true" />
|
app:useSimpleSummaryProvider="true" />
|
||||||
|
<EditTextPreference
|
||||||
|
app:icon="@drawable/ic_baseline_person_24"
|
||||||
|
app:key="serverUsername"
|
||||||
|
app:title="@string/uuid"
|
||||||
|
app:useSimpleSummaryProvider="true" />
|
||||||
<EditTextPreference
|
<EditTextPreference
|
||||||
app:dialogLayout="@layout/layout_password_dialog"
|
app:dialogLayout="@layout/layout_password_dialog"
|
||||||
app:icon="@drawable/ic_settings_password"
|
app:icon="@drawable/ic_settings_password"
|
||||||
app:key="serverPassword"
|
app:key="serverPassword"
|
||||||
app:title="@string/tuic_token" />
|
app:title="@string/password" />
|
||||||
<EditTextPreference
|
<EditTextPreference
|
||||||
app:icon="@drawable/ic_baseline_legend_toggle_24"
|
app:icon="@drawable/ic_baseline_legend_toggle_24"
|
||||||
app:key="serverALPN"
|
app:key="serverALPN"
|
||||||
@ -77,6 +91,11 @@
|
|||||||
app:key="serverMTU"
|
app:key="serverMTU"
|
||||||
app:title="@string/mtu"
|
app:title="@string/mtu"
|
||||||
app:useSimpleSummaryProvider="true" />
|
app:useSimpleSummaryProvider="true" />
|
||||||
|
<moe.matsuri.nb4a.ui.EditConfigPreference
|
||||||
|
app:icon="@drawable/ic_baseline_layers_24"
|
||||||
|
app:key="serverConfig"
|
||||||
|
app:title="@string/custom_config"
|
||||||
|
app:useSimpleSummaryProvider="true" />
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
Loading…
Reference in New Issue
Block a user