Compare commits

...

16 Commits

Author SHA1 Message Date
2dust d89238e1aa up 1.8.14 2024-01-19 18:56:44 +08:00
2dust 52710020ea update subscription 4 custom configuration 2024-01-19 17:44:35 +08:00
2dust a18b8f4f2b Bug fix 2024-01-19 10:29:43 +08:00
2dust 01e0a6570d Merge pull request #2768 from solokot/master
Update Russian translation
2024-01-19 08:36:22 +08:00
solokot cd6ef8f062 Update Russian translation
MTU menu
2024-01-18 08:38:45 +03:00
2dust ea383c43bd Add share 4 Wireguard 2024-01-18 11:56:03 +08:00
2dust 68e08e3866 Merge pull request #2763 from admarty/master
Update vi translation
2024-01-18 09:57:21 +08:00
admarty 329ed26e85 Update vi translation 2024-01-17 12:56:39 +07:00
admarty c830a4cea4 Revert "Updated vi translation"
This reverts commit e7e088ec83.
2024-01-17 12:53:53 +07:00
admarty e7e088ec83 Updated vi translation 2024-01-17 12:46:48 +07:00
2dust cf6c814eb4 Add share 4 Wireguard 2024-01-16 18:49:00 +08:00
2dust 8f62e42ae8 Add Wireguard Mtu 2024-01-16 17:48:04 +08:00
2dust 89b4060ba2 Merge pull request #2748 from admarty/master
Improve vietnamese translation
2024-01-11 17:53:51 +08:00
admarty e4d4b329a0 fix typo 2024-01-11 13:03:53 +07:00
admarty 09c5f41995 Improve Vietnamese translation 2024-01-11 12:04:57 +07:00
admarty 02b25787c1 Improve Vietnamese translation 2024-01-11 00:48:28 +07:00
18 changed files with 294 additions and 131 deletions
+2 -2
View File
@@ -17,8 +17,8 @@ android {
minSdkVersion 21
targetSdkVersion Integer.parseInt("$targetSdkVer")
multiDexEnabled true
versionCode 538
versionName "1.8.13"
versionCode 539
versionName "1.8.14"
}
buildTypes {
@@ -62,8 +62,10 @@ object AppConfig {
const val TAG_DIRECT = "direct"
const val TAG_BLOCKED = "block"
const val androidpackagenamelistUrl = "https://raw.githubusercontent.com/2dust/androidpackagenamelist/master/proxy.txt"
const val v2rayCustomRoutingListUrl = "https://raw.githubusercontent.com/2dust/v2rayCustomRoutingList/master/"
const val androidpackagenamelistUrl =
"https://raw.githubusercontent.com/2dust/androidpackagenamelist/master/proxy.txt"
const val v2rayCustomRoutingListUrl =
"https://raw.githubusercontent.com/2dust/v2rayCustomRoutingList/master/"
const val v2rayNGIssues = "https://github.com/2dust/v2rayNG/issues"
const val v2rayNGWikiMode = "https://github.com/2dust/v2rayNG/wiki/Mode"
const val v2rayNGPrivacyPolicy = "https://raw.githubusercontent.com/2dust/v2rayNG/master/CR.md"
@@ -76,6 +78,9 @@ object AppConfig {
const val PORT_LOCAL_DNS = "10853"
const val PORT_SOCKS = "10808"
const val PORT_HTTP = "10809"
const val WIREGUARD_LOCAL_ADDRESS_V4 = "172.16.0.2/32"
const val WIREGUARD_LOCAL_ADDRESS_V6 = "2606:4700:110:8f81:d551:a0:532e:a2b3/128"
const val WIREGUARD_LOCAL_MTU = "1420"
const val MSG_REGISTER_CLIENT = 1
const val MSG_STATE_RUNNING = 11
@@ -86,6 +86,7 @@ data class V2rayConfig(
var secretKey: String? = null,
val peers: List<WireGuardBean>? = null,
var reserved: List<Int>? = null,
var mtu :Int? = null
) {
data class VnextBean(var address: String = "",
@@ -17,16 +17,16 @@ val Context.v2RayApplication: AngApplication
get() = applicationContext as AngApplication
fun Context.toast(message: Int): Toast = ToastCompat
.makeText(this, message, Toast.LENGTH_SHORT)
.apply {
show()
}
.makeText(this, message, Toast.LENGTH_SHORT)
.apply {
show()
}
fun Context.toast(message: CharSequence): Toast = ToastCompat
.makeText(this, message, Toast.LENGTH_SHORT)
.apply {
show()
}
.makeText(this, message, Toast.LENGTH_SHORT)
.apply {
show()
}
fun JSONObject.putOpt(pair: Pair<String, Any>) = putOpt(pair.first, pair.second)
fun JSONObject.putOpt(pairs: Map<String, Any>) = pairs.forEach { putOpt(it.key to it.value) }
@@ -77,4 +77,8 @@ val URLConnection.responseLength: Long
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) contentLengthLong else contentLength.toLong()
val URI.idnHost: String
get() = (host!!).replace("[", "").replace("]", "")
get() = (host!!).replace("[", "").replace("]", "")
fun String.removeWhiteSpace(): String {
return this.replace(" ", "")
}
@@ -73,5 +73,8 @@ object SubscriptionUpdater {
if (count <= 0) {
AngConfigManager.importBatchConfig(Utils.decode(server!!), subid, append)
}
if (count <= 0) {
AngConfigManager.appendCustomConfigServer(server, subid)
}
}
}
@@ -419,6 +419,9 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
if (count <= 0) {
count = AngConfigManager.importBatchConfig(Utils.decode(server!!), subid2, append)
}
if (count <= 0) {
count = AngConfigManager.appendCustomConfigServer(server, subid2)
}
if (count > 0) {
toast(R.string.toast_success)
mainViewModel.reloadServerList()
@@ -10,12 +10,16 @@ import androidx.appcompat.app.AlertDialog
import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig
import com.v2ray.ang.AppConfig.PREF_ALLOW_INSECURE
import com.v2ray.ang.AppConfig.WIREGUARD_LOCAL_ADDRESS_V4
import com.v2ray.ang.AppConfig.WIREGUARD_LOCAL_ADDRESS_V6
import com.v2ray.ang.AppConfig.WIREGUARD_LOCAL_MTU
import com.v2ray.ang.R
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.dto.ServerConfig
import com.v2ray.ang.dto.V2rayConfig
import com.v2ray.ang.dto.V2rayConfig.Companion.DEFAULT_PORT
import com.v2ray.ang.dto.V2rayConfig.Companion.TLS
import com.v2ray.ang.extension.removeWhiteSpace
import com.v2ray.ang.extension.toast
import com.v2ray.ang.util.MmkvManager
import com.v2ray.ang.util.MmkvManager.ID_MAIN
@@ -26,7 +30,12 @@ import com.v2ray.ang.util.Utils.getIpv6Address
class ServerActivity : BaseActivity() {
private val mainStorage by lazy { MMKV.mmkvWithID(ID_MAIN, MMKV.MULTI_PROCESS_MODE) }
private val settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) }
private val settingsStorage by lazy {
MMKV.mmkvWithID(
MmkvManager.ID_SETTING,
MMKV.MULTI_PROCESS_MODE
)
}
private val editGuid by lazy { intent.getStringExtra("guid").orEmpty() }
private val isRunning by lazy {
intent.getBooleanExtra("isRunning", false)
@@ -34,7 +43,8 @@ class ServerActivity : BaseActivity() {
&& editGuid == mainStorage?.decodeString(KEY_SELECTED_SERVER)
}
private val createConfigType by lazy {
EConfigType.fromInt(intent.getIntExtra("createConfigType", EConfigType.VMESS.value)) ?: EConfigType.VMESS
EConfigType.fromInt(intent.getIntExtra("createConfigType", EConfigType.VMESS.value))
?: EConfigType.VMESS
}
private val subscriptionId by lazy {
intent.getStringExtra("subscriptionId")
@@ -67,12 +77,13 @@ class ServerActivity : BaseActivity() {
private val allowinsecures: Array<out String> by lazy {
resources.getStringArray(R.array.allowinsecures)
}
private val uTlsItems: Array<out String> by lazy {
private val uTlsItems: Array<out String> by lazy {
resources.getStringArray(R.array.streamsecurity_utls)
}
private val alpns: Array<out String> by lazy {
resources.getStringArray(R.array.streamsecurity_alpn)
}
// Kotlin synthetics was used, but since it is removed in 1.8. We switch to old manual approach.
// We don't use AndroidViewBinding because, it is better to share similar logics for different
// protocols. Use findViewById manually ensures the xml are de-coupled with the activity logic.
@@ -107,8 +118,8 @@ class ServerActivity : BaseActivity() {
private val et_reserved1: EditText? by lazy { findViewById(R.id.et_reserved1) }
private val et_reserved2: EditText? by lazy { findViewById(R.id.et_reserved2) }
private val et_reserved3: EditText? by lazy { findViewById(R.id.et_reserved3) }
private val et_local_v4_address: EditText? by lazy { findViewById(R.id.et_local_v4_address) }
private val et_local_v6_address: EditText? by lazy { findViewById(R.id.et_local_v6_address) }
private val et_local_address: EditText? by lazy { findViewById(R.id.et_local_address) }
private val et_local_mtu: EditText? by lazy { findViewById(R.id.et_local_mtu) }
override fun onCreate(savedInstanceState: Bundle?) {
@@ -116,7 +127,7 @@ class ServerActivity : BaseActivity() {
title = getString(R.string.title_server)
val config = MmkvManager.decodeServerConfig(editGuid)
when(config?.configType ?: createConfigType) {
when (config?.configType ?: createConfigType) {
EConfigType.VMESS -> setContentView(R.layout.activity_server_vmess)
EConfigType.CUSTOM -> return
EConfigType.SHADOWSOCKS -> setContentView(R.layout.activity_server_shadowsocks)
@@ -126,10 +137,16 @@ class ServerActivity : BaseActivity() {
EConfigType.WIREGUARD -> setContentView(R.layout.activity_server_wireguard)
}
sp_network?.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
override fun onItemSelected(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
val types = transportTypes(networks[position])
sp_header_type?.isEnabled = types.size > 1
val adapter = ArrayAdapter(this@ServerActivity, android.R.layout.simple_spinner_item, types)
val adapter =
ArrayAdapter(this@ServerActivity, android.R.layout.simple_spinner_item, types)
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
sp_header_type?.adapter = adapter
sp_header_type_title?.text = if (networks[position] == "grpc")
@@ -141,12 +158,18 @@ class ServerActivity : BaseActivity() {
et_path?.text = Utils.getEditable(transportDetails[2])
}
}
override fun onNothingSelected(parent: AdapterView<*>?) {
// do nothing
}
}
sp_stream_security?.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
override fun onItemSelected(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
if (streamSecuritys[position].isBlank()) {
container_sni?.visibility = View.GONE
container_fingerprint?.visibility = View.GONE
@@ -173,6 +196,7 @@ class ServerActivity : BaseActivity() {
}
}
}
override fun onNothingSelected(p0: AdapterView<*>?) {
// do nothing
}
@@ -192,39 +216,55 @@ class ServerActivity : BaseActivity() {
et_remarks.text = Utils.getEditable(config.remarks)
et_address.text = Utils.getEditable(outbound.getServerAddress().orEmpty())
et_port.text = Utils.getEditable(outbound.getServerPort()?.toString() ?: DEFAULT_PORT.toString())
et_port.text =
Utils.getEditable(outbound.getServerPort()?.toString() ?: DEFAULT_PORT.toString())
et_id.text = Utils.getEditable(outbound.getPassword().orEmpty())
et_alterId?.text = Utils.getEditable(outbound.settings?.vnext?.get(0)?.users?.get(0)?.alterId.toString())
et_alterId?.text =
Utils.getEditable(outbound.settings?.vnext?.get(0)?.users?.get(0)?.alterId.toString())
if (config.configType == EConfigType.SOCKS) {
et_security?.text = Utils.getEditable(outbound.settings?.servers?.get(0)?.users?.get(0)?.user.orEmpty())
et_security?.text =
Utils.getEditable(outbound.settings?.servers?.get(0)?.users?.get(0)?.user.orEmpty())
} else if (config.configType == EConfigType.VLESS) {
et_security?.text = Utils.getEditable(outbound.getSecurityEncryption().orEmpty())
val flow = Utils.arrayFind(flows, outbound.settings?.vnext?.get(0)?.users?.get(0)?.flow.orEmpty())
val flow = Utils.arrayFind(
flows,
outbound.settings?.vnext?.get(0)?.users?.get(0)?.flow.orEmpty()
)
if (flow >= 0) {
sp_flow?.setSelection(flow)
}
} else if (config.configType == EConfigType.WIREGUARD) {
et_public_key?.text = Utils.getEditable(outbound.settings?.peers?.get(0)?.publicKey.orEmpty())
et_public_key?.text =
Utils.getEditable(outbound.settings?.peers?.get(0)?.publicKey.orEmpty())
if (outbound.settings?.reserved == null) {
et_reserved1?.text = Utils.getEditable("0")
et_reserved2?.text = Utils.getEditable("0")
et_reserved3?.text = Utils.getEditable("0")
} else {
et_reserved1?.text = Utils.getEditable(outbound.settings?.reserved?.get(0).toString())
et_reserved2?.text = Utils.getEditable(outbound.settings?.reserved?.get(1).toString())
et_reserved3?.text = Utils.getEditable(outbound.settings?.reserved?.get(2).toString())
et_reserved1?.text =
Utils.getEditable(outbound.settings?.reserved?.get(0).toString())
et_reserved2?.text =
Utils.getEditable(outbound.settings?.reserved?.get(1).toString())
et_reserved3?.text =
Utils.getEditable(outbound.settings?.reserved?.get(2).toString())
}
if (outbound.settings?.address == null) {
et_local_v4_address?.text = Utils.getEditable("172.16.0.2/32")
et_local_v6_address?.text = Utils.getEditable("2606:4700:110:8f81:d551:a0:532e:a2b3/128")
et_local_address?.text =
Utils.getEditable("${WIREGUARD_LOCAL_ADDRESS_V4},${WIREGUARD_LOCAL_ADDRESS_V6}")
} else {
val list = outbound.settings?.address as List<*>
et_local_v4_address?.text = Utils.getEditable(list.get(0).toString())
et_local_v6_address?.text = Utils.getEditable(list.get(1).toString())
et_local_address?.text = Utils.getEditable(list.joinToString())
}
if (outbound.settings?.mtu == null) {
et_local_mtu?.text = Utils.getEditable(WIREGUARD_LOCAL_MTU)
} else {
et_local_mtu?.text = Utils.getEditable(outbound.settings?.mtu.toString())
}
}
val securityEncryptions = if (config.configType == EConfigType.SHADOWSOCKS) shadowsocksSecuritys else securitys
val security = Utils.arrayFind(securityEncryptions, outbound.getSecurityEncryption().orEmpty())
val securityEncryptions =
if (config.configType == EConfigType.SHADOWSOCKS) shadowsocksSecuritys else securitys
val security =
Utils.arrayFind(securityEncryptions, outbound.getSecurityEncryption().orEmpty())
if (security >= 0) {
sp_security?.setSelection(security)
}
@@ -233,7 +273,7 @@ class ServerActivity : BaseActivity() {
val streamSecurity = Utils.arrayFind(streamSecuritys, streamSetting.security)
if (streamSecurity >= 0) {
sp_stream_security?.setSelection(streamSecurity)
(streamSetting.tlsSettings?: streamSetting.realitySettings)?.let { tlsSetting ->
(streamSetting.tlsSettings ?: streamSetting.realitySettings)?.let { tlsSetting ->
container_sni?.visibility = View.VISIBLE
container_fingerprint?.visibility = View.VISIBLE
container_alpn?.visibility = View.VISIBLE
@@ -243,12 +283,16 @@ class ServerActivity : BaseActivity() {
sp_stream_fingerprint?.setSelection(utlsIndex)
}
tlsSetting.alpn?.let {
val alpnIndex = Utils.arrayFind(alpns, Utils.removeWhiteSpace(tlsSetting.alpn.joinToString())!!)
val alpnIndex = Utils.arrayFind(
alpns,
Utils.removeWhiteSpace(tlsSetting.alpn.joinToString())!!
)
sp_stream_alpn?.setSelection(alpnIndex)
}
if (streamSetting.tlsSettings != null) {
container_allow_insecure?.visibility = View.VISIBLE
val allowinsecure = Utils.arrayFind(allowinsecures, tlsSetting.allowInsecure.toString())
val allowinsecure =
Utils.arrayFind(allowinsecures, tlsSetting.allowInsecure.toString())
if (allowinsecure >= 0) {
sp_allow_insecure?.setSelection(allowinsecure)
}
@@ -307,8 +351,9 @@ class ServerActivity : BaseActivity() {
et_reserved1?.text = Utils.getEditable("0")
et_reserved2?.text = Utils.getEditable("0")
et_reserved3?.text = Utils.getEditable("0")
et_local_v4_address?.text = Utils.getEditable("172.16.0.2/32")
et_local_v6_address?.text = Utils.getEditable("2606:4700:110:8f81:d551:a0:532e:a2b3/128")
et_local_address?.text =
Utils.getEditable("${WIREGUARD_LOCAL_ADDRESS_V4},${WIREGUARD_LOCAL_ADDRESS_V6}")
et_local_mtu?.text = Utils.getEditable(WIREGUARD_LOCAL_MTU)
return true
}
@@ -329,11 +374,12 @@ class ServerActivity : BaseActivity() {
toast(R.string.server_lab_port)
return false
}
val config = MmkvManager.decodeServerConfig(editGuid) ?: ServerConfig.create(createConfigType)
val config =
MmkvManager.decodeServerConfig(editGuid) ?: ServerConfig.create(createConfigType)
if (config.configType != EConfigType.SOCKS && TextUtils.isEmpty(et_id.text.toString())) {
if (config.configType == EConfigType.TROJAN || config.configType == EConfigType.SHADOWSOCKS) {
toast(R.string.server_lab_id3)
}else{
} else {
toast(R.string.server_lab_id)
}
return false
@@ -366,7 +412,7 @@ class ServerActivity : BaseActivity() {
config.outboundBean?.streamSettings?.let {
saveStreamSettings(it)
}
if(config.subscriptionId.isEmpty() && !subscriptionId.isNullOrEmpty()) {
if (config.subscriptionId.isEmpty() && !subscriptionId.isNullOrEmpty()) {
config.subscriptionId = subscriptionId!!
}
@@ -376,7 +422,11 @@ class ServerActivity : BaseActivity() {
return true
}
private fun saveVnext(vnext: V2rayConfig.OutboundBean.OutSettingsBean.VnextBean, port: Int, config: ServerConfig) {
private fun saveVnext(
vnext: V2rayConfig.OutboundBean.OutSettingsBean.VnextBean,
port: Int,
config: ServerConfig
) {
vnext.address = et_address.text.toString().trim()
vnext.port = port
vnext.users[0].id = et_id.text.toString().trim()
@@ -389,7 +439,11 @@ class ServerActivity : BaseActivity() {
}
}
private fun saveServers(server: V2rayConfig.OutboundBean.OutSettingsBean.ServersBean, port: Int, config: ServerConfig) {
private fun saveServers(
server: V2rayConfig.OutboundBean.OutSettingsBean.ServersBean,
port: Int,
config: ServerConfig
) {
server.address = et_address.text.toString().trim()
server.port = port
if (config.configType == EConfigType.SHADOWSOCKS) {
@@ -399,7 +453,8 @@ class ServerActivity : BaseActivity() {
if (TextUtils.isEmpty(et_security?.text) && TextUtils.isEmpty(et_id.text)) {
server.users = null
} else {
val socksUsersBean = V2rayConfig.OutboundBean.OutSettingsBean.ServersBean.SocksUsersBean()
val socksUsersBean =
V2rayConfig.OutboundBean.OutSettingsBean.ServersBean.SocksUsersBean()
socksUsersBean.user = et_security?.text.toString().trim()
socksUsersBean.pass = et_id.text.toString().trim()
server.users = listOf(socksUsersBean)
@@ -412,17 +467,18 @@ class ServerActivity : BaseActivity() {
private fun savePeer(wireguard: V2rayConfig.OutboundBean.OutSettingsBean, port: Int) {
wireguard.secretKey = et_id.text.toString().trim()
wireguard.peers?.get(0)?.publicKey = et_public_key?.text.toString().trim()
wireguard.peers?.get(0)?.endpoint = getIpv6Address(et_address.text.toString().trim()) + ":" + port
wireguard.peers?.get(0)?.endpoint =
getIpv6Address(et_address.text.toString().trim()) + ":" + port
val reserved1 = Utils.parseInt(et_reserved1?.text.toString())
val reserved2 = Utils.parseInt(et_reserved2?.text.toString())
val reserved3 = Utils.parseInt(et_reserved3?.text.toString())
if (reserved1 > 0 || reserved2 > 0 || reserved3 > 0) {
wireguard.reserved = listOf(reserved1, reserved2, reserved3)
}else {
} else {
wireguard.reserved = null
}
wireguard.address = listOf(et_local_v4_address?.text.toString().trim(),
et_local_v6_address?.text.toString().trim())
wireguard.address = et_local_address?.text.toString().removeWhiteSpace().split(",")
wireguard.mtu = Utils.parseInt(et_local_mtu?.text.toString())
}
private fun saveStreamSettings(streamSetting: V2rayConfig.OutboundBean.StreamSettingsBean) {
@@ -440,15 +496,15 @@ class ServerActivity : BaseActivity() {
val spiderX = et_spider_x?.text?.toString()?.trim() ?: return
var sni = streamSetting.populateTransportSettings(
transport = networks[network],
headerType = transportTypes(networks[network])[type],
host = requestHost,
path = path,
seed = path,
quicSecurity = requestHost,
key = path,
mode = transportTypes(networks[network])[type],
serviceName = path
transport = networks[network],
headerType = transportTypes(networks[network])[type],
host = requestHost,
path = path,
seed = path,
quicSecurity = requestHost,
key = path,
mode = transportTypes(networks[network])[type],
serviceName = path
)
if (sniField.isNotBlank()) {
sni = sniField
@@ -460,14 +516,14 @@ class ServerActivity : BaseActivity() {
}
streamSetting.populateTlsSettings(
streamSecurity = streamSecuritys[streamSecurity],
allowInsecure = allowInsecure,
sni = sni,
fingerprint = uTlsItems[utlsIndex],
alpns = alpns[alpnIndex],
publicKey = publicKey,
shortId = shortId,
spiderX = spiderX
streamSecurity = streamSecuritys[streamSecurity],
allowInsecure = allowInsecure,
sni = sni,
fingerprint = uTlsItems[utlsIndex],
alpns = alpns[alpnIndex],
publicKey = publicKey,
shortId = shortId,
spiderX = spiderX
)
}
@@ -476,12 +532,15 @@ class ServerActivity : BaseActivity() {
"tcp" -> {
tcpTypes
}
"kcp", "quic" -> {
kcpAndQuicTypes
}
"grpc" -> {
grpcModes
}
else -> {
arrayOf("---")
}
@@ -532,10 +591,12 @@ class ServerActivity : BaseActivity() {
deleteServer()
true
}
R.id.save_config -> {
saveServer()
true
}
else -> super.onOptionsItemSelected(item)
}
}
@@ -12,6 +12,8 @@ import com.v2ray.ang.AppConfig
import com.v2ray.ang.AppConfig.ANG_CONFIG
import com.v2ray.ang.AppConfig.HTTPS_PROTOCOL
import com.v2ray.ang.AppConfig.HTTP_PROTOCOL
import com.v2ray.ang.AppConfig.WIREGUARD_LOCAL_ADDRESS_V4
import com.v2ray.ang.AppConfig.WIREGUARD_LOCAL_MTU
import com.v2ray.ang.R
import com.v2ray.ang.dto.*
import com.v2ray.ang.dto.V2rayConfig.Companion.DEFAULT_SECURITY
@@ -20,6 +22,7 @@ import com.v2ray.ang.util.MmkvManager.KEY_SELECTED_SERVER
import java.net.URI
import java.util.*
import com.v2ray.ang.extension.idnHost
import com.v2ray.ang.extension.removeWhiteSpace
import com.v2ray.ang.extension.toast
object AngConfigManager {
@@ -432,6 +435,28 @@ object AngConfigManager {
queryParam["security"] ?: "", allowInsecure,
queryParam["sni"] ?: sni, fingerprint, queryParam["alpn"], pbk, sid, spx
)
} else if (str.startsWith(EConfigType.WIREGUARD.protocolScheme)) {
val uri = URI(Utils.fixIllegalUrl(str))
config = ServerConfig.create(EConfigType.WIREGUARD)
config.remarks = Utils.urlDecode(uri.fragment ?: "")
if (uri.rawQuery != null) {
val queryParam = uri.rawQuery.split("&")
.associate { it.split("=").let { (k, v) -> k to Utils.urlDecode(v) } }
config.outboundBean?.settings?.let { wireguard ->
wireguard.secretKey = uri.userInfo
wireguard.address =
(queryParam["address"] ?: WIREGUARD_LOCAL_ADDRESS_V4).removeWhiteSpace()
.split(",")
wireguard.peers?.get(0)?.publicKey = queryParam["publickey"] ?: ""
wireguard.peers?.get(0)?.endpoint = "${uri.idnHost}:${uri.port}"
wireguard.mtu = Utils.parseInt(queryParam["mtu"] ?: WIREGUARD_LOCAL_MTU)
wireguard.reserved =
(queryParam["reserved"] ?: "0,0,0").removeWhiteSpace().split(",")
.map { it.toInt() }
}
}
}
if (config == null) {
return R.string.toast_incorrect_protocol
@@ -443,7 +468,8 @@ object AngConfigManager {
?.getServerAddress() == removedSelectedServer.getProxyOutbound()
?.getServerAddress() &&
config.getProxyOutbound()
?.getServerPort() == removedSelectedServer.getProxyOutbound()?.getServerPort()
?.getServerPort() == removedSelectedServer.getProxyOutbound()
?.getServerPort()
) {
mainStorage?.encode(KEY_SELECTED_SERVER, guid)
}
@@ -571,7 +597,11 @@ object AngConfigManager {
try {
val config = MmkvManager.decodeServerConfig(guid) ?: return ""
val outbound = config.getProxyOutbound() ?: return ""
val streamSetting = outbound.streamSettings ?: return ""
val streamSetting =
outbound.streamSettings ?: V2rayConfig.OutboundBean.StreamSettingsBean()
if (config.configType != EConfigType.WIREGUARD) {
if (outbound.streamSettings == null) return ""
}
return config.configType.protocolScheme + when (config.configType) {
EConfigType.VMESS -> {
val vmessQRCode = VmessQRCode()
@@ -600,7 +630,8 @@ object AngConfigManager {
Utils.encode(json)
}
EConfigType.CUSTOM, EConfigType.WIREGUARD -> ""
EConfigType.CUSTOM -> ""
EConfigType.SHADOWSOCKS -> {
val remark = "#" + Utils.urlEncode(config.remarks)
val pw =
@@ -675,7 +706,8 @@ object AngConfigManager {
dicQuery["spx"] = Utils.urlEncode(tlsSetting.spiderX!!)
}
}
dicQuery["type"] = streamSetting.network.ifEmpty { V2rayConfig.DEFAULT_NETWORK }
dicQuery["type"] =
streamSetting.network.ifEmpty { V2rayConfig.DEFAULT_NETWORK }
outbound.getTransportSettingDetails()?.let { transportDetails ->
when (streamSetting.network) {
@@ -736,6 +768,36 @@ object AngConfigManager {
)
url + query + remark
}
EConfigType.WIREGUARD -> {
val remark = "#" + Utils.urlEncode(config.remarks)
val dicQuery = HashMap<String, String>()
dicQuery["publickey"] =
Utils.urlEncode(outbound.settings?.peers?.get(0)?.publicKey.toString())
dicQuery["reserved"] = Utils.urlEncode(
Utils.removeWhiteSpace(outbound.settings?.reserved?.joinToString())
.toString()
)
dicQuery["address"] = Utils.urlEncode(
Utils.removeWhiteSpace((outbound.settings?.address as List<*>).joinToString())
.toString()
)
if (outbound.settings?.mtu != null) {
dicQuery["mtu"] = outbound.settings?.mtu.toString()
}
val query = "?" + dicQuery.toList().joinToString(
separator = "&",
transform = { it.first + "=" + it.second })
val url = String.format(
"%s@%s:%s",
Utils.urlEncode(outbound.getPassword().toString()),
Utils.getIpv6Address(outbound.getServerAddress()!!),
outbound.getServerPort()
)
url + query + remark
}
}
} catch (e: Exception) {
e.printStackTrace()
@@ -861,17 +923,19 @@ object AngConfigManager {
return 0
}
val removedSelectedServer =
if (!TextUtils.isEmpty(subid) && !append) {
MmkvManager.decodeServerConfig(mainStorage?.decodeString(KEY_SELECTED_SERVER) ?: "")?.let {
if (it.subscriptionId == subid) {
return@let it
}
return@let null
if (!TextUtils.isEmpty(subid) && !append) {
MmkvManager.decodeServerConfig(
mainStorage?.decodeString(KEY_SELECTED_SERVER) ?: ""
)?.let {
if (it.subscriptionId == subid) {
return@let it
}
} else {
null
return@let null
}
if(!append) {
} else {
null
}
if (!append) {
MmkvManager.removeServerViaSubid(subid)
}
// var servers = server
@@ -911,4 +975,24 @@ object AngConfigManager {
return true
}
fun appendCustomConfigServer(server: String?, subid: String): Int {
if (server == null) {
return 0
}
if (server.contains("inbounds")
&& server.contains("outbounds")
&& server.contains("routing")
) {
val config = ServerConfig.create(EConfigType.CUSTOM)
config.remarks = System.currentTimeMillis().toString()
config.subscriptionId = subid
config.fullConfig = Gson().fromJson(server, V2rayConfig::class.java)
val key = MmkvManager.encodeServerConfig("", config)
serverRawStorage?.encode(key, server)
return 1
} else {
return 0
}
}
}
@@ -283,7 +283,7 @@ object Utils {
fun urlDecode(url: String): String {
return try {
URLDecoder.decode(URLDecoder.decode(url), "utf-8")
URLDecoder.decode(url, "UTF-8")
} catch (e: Exception) {
e.printStackTrace()
url
@@ -7,6 +7,8 @@ import com.google.gson.*
import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig
import com.v2ray.ang.AppConfig.ANG_PACKAGE
import com.v2ray.ang.AppConfig.WIREGUARD_LOCAL_ADDRESS_V4
import com.v2ray.ang.AppConfig.WIREGUARD_LOCAL_ADDRESS_V6
import com.v2ray.ang.dto.V2rayConfig
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.dto.ERoutingMode
@@ -32,12 +34,12 @@ object V2rayConfigUtil {
} else {
raw
}
Log.d(ANG_PACKAGE, customConfig)
//Log.d(ANG_PACKAGE, customConfig)
return Result(true, customConfig)
}
val outbound = config.getProxyOutbound() ?: return Result(false, "")
val result = getV2rayNonCustomConfig(context, outbound)
Log.d(ANG_PACKAGE, result.content)
//Log.d(ANG_PACKAGE, result.content)
return result
} catch (e: Exception) {
e.printStackTrace()
@@ -428,7 +430,7 @@ object V2rayConfigUtil {
if (protocol.equals(EConfigType.WIREGUARD.name, true)) {
var localTunAddr = if (outbound.settings?.address == null) {
listOf("172.16.0.2/32", "2606:4700:110:8f81:d551:a0:532e:a2b3/128")
listOf(WIREGUARD_LOCAL_ADDRESS_V4, WIREGUARD_LOCAL_ADDRESS_V6)
} else {
outbound.settings?.address as List<*>
}
@@ -88,11 +88,10 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/server_lab_public_key" />
android:text="@string/server_lab_secret_key" />
<EditText
android:id="@+id/et_public_key"
android:id="@+id/et_id"
android:layout_width="match_parent"
android:layout_height="@dimen/edit_height"
android:inputType="text" />
@@ -108,10 +107,11 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/server_lab_secret_key" />
android:text="@string/server_lab_public_key" />
<EditText
android:id="@+id/et_id"
android:id="@+id/et_public_key"
android:layout_width="match_parent"
android:layout_height="@dimen/edit_height"
android:inputType="text" />
@@ -160,14 +160,13 @@
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/server_lab_local_v4_address" />
android:text="@string/server_lab_local_address" />
<EditText
android:id="@+id/et_local_v4_address"
android:id="@+id/et_local_address"
android:layout_width="match_parent"
android:layout_height="@dimen/edit_height"
android:inputType="text" />
</LinearLayout>
<LinearLayout
@@ -179,13 +178,13 @@
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/server_lab_local_v6_address" />
android:text="@string/server_lab_local_mtu" />
<EditText
android:id="@+id/et_local_v6_address"
android:id="@+id/et_local_mtu"
android:layout_width="match_parent"
android:layout_height="@dimen/edit_height"
android:inputType="text" />
android:inputType="number" />
</LinearLayout>
@@ -64,8 +64,8 @@
<string name="server_lab_short_id" translatable="false">ShortId</string>
<string name="server_lab_spider_x" translatable="false">SpiderX</string>
<string name="server_lab_reserved">Reserved (اختياري)</string>
<string name="server_lab_local_v4_address">العنوان المحلي IPv4(اختياري)</string>
<string name="server_lab_local_v6_address">العنوان المحلي IPv6(اختياري, يستخدم فقط عندما تفضل IPv6)</string>
<string name="server_lab_local_address">العنوان المحلي IPv4(اختياري)</string>
<string name="server_lab_local_mtu">Mtu(optional, default 1420)</string>
<string name="toast_success">نجاح</string>
<string name="toast_failure">فشل</string>
<string name="toast_none_data">لا يوجد شيء</string>
@@ -61,8 +61,8 @@
<string name="server_lab_encryption">رمزگذاری</string>
<string name="server_lab_flow">جریان</string>
<string name="server_lab_reserved">Reserved (اختیاری)</string>
<string name="server_lab_local_v4_address">آدرس محلی IPv4(اختیاری)</string>
<string name="server_lab_local_v6_address">آدرس محلی IPv6(اختیاری, فقط زمانی استفاده می شود که IPv6 را ترجیح می دهد)</string>
<string name="server_lab_local_address">آدرس محلی IPv4(اختیاری)</string>
<string name="server_lab_local_mtu">Mtu(optional, default 1420)</string>
<string name="toast_success">موفقیت</string>
<string name="toast_failure">شکست</string>
<string name="toast_none_data">چیزی نیست</string>
@@ -65,9 +65,10 @@
<string name="server_lab_public_key" translatable="false">PublicKey</string>
<string name="server_lab_short_id" translatable="false">ShortId</string>
<string name="server_lab_spider_x" translatable="false">SpiderX</string>
<string name="server_lab_secret_key" translatable="false">SecretKey</string>
<string name="server_lab_reserved">Reserved (необязательно)</string>
<string name="server_lab_local_v4_address">Локальный адрес IPv4 (необязательно)</string>
<string name="server_lab_local_v6_address">Локальный адрес IPv6 (необязательно, используется только если предпочитаете IPv6)</string>
<string name="server_lab_local_address">Локальный адрес (необязательно, IPv4/IPv6 через запятую)</string>
<string name="server_lab_local_mtu">MTU (необязательно, по умолчанию 1420)</string>
<string name="toast_success">Успешно</string>
<string name="toast_failure">Ошибка</string>
<string name="toast_none_data">Ничего нет</string>
+23 -23
View File
@@ -12,10 +12,10 @@
<string name="notification_action_stop_v2ray">Ngắt kết nối v2rayNG</string>
<string name="toast_permission_denied">Vui lòng cấp quyền cần thiết cho v2rayNG! Bạn đã từ chối các quyền cần thiết như Camera hay Bộ nhớ?</string>
<string name="notification_action_more">Nhấn để biết thêm</string>
<string name="toast_services_start">Đang bắt đầu v2rayNG...</string>
<string name="toast_services_start">Đang khởi động v2rayNG...</string>
<string name="toast_services_stop">Đã dừng v2rayNG!</string>
<string name="toast_services_success">Đã bắt đầu v2rayNG!</string>
<string name="toast_services_failure">Không thể bắt đầu v2rayNG, kiểm tra lại cấu hình.</string>
<string name="toast_services_success">Đã khởi động v2rayNG!</string>
<string name="toast_services_failure">Không thể khởi động v2rayNG, kiểm tra lại cấu hình.</string>
<!--ServerActivity-->
<string name="title_server">v2rayNG</string>
@@ -41,7 +41,7 @@
<string name="server_lab_port">Cổng</string>
<string name="server_lab_id">ID</string>
<string name="server_lab_alterid">alterId</string>
<string name="server_lab_security">Phương thức mã hóa</string>
<string name="server_lab_security">Thuật toán mã hóa</string>
<string name="server_lab_network">Giao thức truyền tải (network)</string>
<string name="server_lab_more_function">Nâng cao</string>
<string name="server_lab_head_type">Kiểu ngụy trang (type)</string>
@@ -54,14 +54,14 @@
<string name="server_lab_address3">Địa chỉ</string>
<string name="server_lab_port3">Cổng</string>
<string name="server_lab_id3">Mật khẩu</string>
<string name="server_lab_security3">Bảo mật</string>
<string name="server_lab_id4">Mật khẩu (Bổ sung)</string>
<string name="server_lab_security4">Tên người dùng (Bổ sung)</string>
<string name="server_lab_encryption">Mã hoá</string>
<string name="server_lab_flow">Kiểm soát lưu lượng</string>
<string name="server_lab_reserved">Reserved (Bổ sung)</string>
<string name="server_lab_local_v4_address">địa chỉ cục bộ IPv4(Bổ sung)</string>
<string name="server_lab_local_v6_address">địa chỉ cục bộ IPv6(Bổ sung, chỉ được sử dụng khi thích IPv6)</string>
<string name="server_lab_security3">Thuật toán mã hóa</string>
<string name="server_lab_id4">Mật khẩu (không bắt buộc)</string>
<string name="server_lab_security4">Tên người dùng (không bắt buộc)</string>
<string name="server_lab_encryption">Mã hóa</string>
<string name="server_lab_flow">Kiểm soát lưu lượng (flow)</string>
<string name="server_lab_reserved">Reserved (không bắt buộc)</string>
<string name="server_lab_local_address">Địa chỉ cục bộ (IPv4/IPv6, phân cách bằng dấu phẩy)</string>
<string name="server_lab_local_mtu">MTU (không bắt buộc, mặc định 1420)</string>
<string name="toast_success">Thành công!</string>
<string name="toast_failure">Đã xảy ra lỗi, vui lòng thử lại!</string>
<string name="toast_none_data">Không có gì ở đây</string>
@@ -76,7 +76,7 @@
<string name="toast_invalid_url">URL không hợp lệ hoặc trống!</string>
<string name="server_lab_need_inbound">Vui lòng đảm bảo cấu hình tùy chỉnh này không bị lỗi trước khi sử dụng!</string>
<string name="toast_malformed_josn">Cấu hình không hợp lệ!</string>
<string name="server_lab_request_host6">Host (SNI) (Bổ sung)</string>
<string name="server_lab_request_host6">Host (SNI) (không bắt buộc)</string>
<string name="toast_asset_copy_failed">Không thể sao chép tệp tin, hãy dùng trình quản lý tệp!</string>
<string name="menu_item_add_file">Thêm tệp</string>
<string name="menu_item_download_file">Tải xuống tệp tin</string>
@@ -101,7 +101,7 @@
<string name="summary_pref_per_app_proxy">- Bình thường: Ứng dụng đã chọn sẽ kết nối thông qua Proxy, chưa lựa chọn sẽ kết nối trực tiếp. \n- Chế độ Bypass: Ứng dụng được chọn sẽ kết nối trực tiếp, không lựa chọn sẽ kết nối qua Proxy. \n- Nếu bạn ở Trung Quốc thì chọn Tự động chọn ứng dụng Proxy trong Menu.</string>
<string name="title_pref_mux_enabled">Bật Mux</string>
<string name="summary_pref_mux_enabled">Dùng để giảm độ trễ trong bước bắt tay của kết nối TCP, Mux phân phối dữ liệu từ nhiều kết nối TCP trên một TCP duy nhất. Không nên sử dụng Mux để xem video, download file hoặc chạy speedtest vì thường không hiệu quả.</string>
<string name="summary_pref_mux_enabled">Giảm độ trễ trong bước bắt tay của kết nối TCP. Mux phân phối dữ liệu từ nhiều kết nối TCP trên một kết nối TCP duy nhất. Không nên sử dụng Mux để xem video, download file hoặc chạy speedtest vì thường không hiệu quả.</string>
<string name="title_pref_mux_concurency">TCP connections (từ 1 đến 1024)</string>
<string name="title_pref_mux_xudp_concurency">XUDP connections (từ 1 đến 1024)</string>
<string name="title_pref_mux_xudp_quic">Handling of QUIC traffic in mux tunnel.</string>
@@ -119,7 +119,7 @@
<string name="summary_pref_sniffing_enabled">Nhận diện tên miền từ gói tin để phục vụ định tuyến. \n(phải tắt để xài Zalo)</string>
<string name="title_pref_local_dns_enabled">Bật Local DNS</string>
<string name="summary_pref_local_dns_enabled">DNS được xử lý bởi mô-đun DNS của xray-core (dùng nếu cần định tuyến Bypassing LAN và địa chỉ nội địa)</string>
<string name="summary_pref_local_dns_enabled">DNS được xử lý bởi mô-đun DNS của xray-core (dùng nếu cần định tuyến bypass cho mạng LAN và địa chỉ nội địa)</string>
<string name="title_pref_fake_dns_enabled">Bật FakeDNS</string>
<string name="summary_pref_fake_dns_enabled">FakeDNS lấy tên miền mục tiêu bằng cách giả mạo DNS (nhanh hơn, nhưng có thể không hoạt động cho một số ứng dụng)</string>
@@ -141,8 +141,8 @@
<string name="summary_pref_domestic_dns">DNS</string>
<string name="title_pref_proxy_sharing_enabled">Cho phép kết nối từ mạng LAN</string>
<string name="summary_pref_proxy_sharing_enabled">Các thiết bị khác có thể kết nối đến Proxy bởi địa chỉ IP thông qua Socks/HTTP, chỉ bật khi bạn tin tưởng kết nối đó.</string>
<string name="toast_warning_pref_proxysharing_short">Cho phép kết nối từ mạng LAN, đảm bảo rằng bạn tin tưởng kết nối hiện tại!</string>
<string name="summary_pref_proxy_sharing_enabled">Các thiết bị khác trong cùng mạng LAN có thể kết nối đến SOCKS/HTTP proxy trên thiết bị của ban. \nChỉ bật tính năng này trong các mạng đáng tin cậy để tránh kết nối trái phép.</string>
<string name="toast_warning_pref_proxysharing_short">Đang bật cho phép kết nối từ mạng LAN</string>
<string name="title_pref_allow_insecure">allowInsecure</string>
<string name="summary_pref_allow_insecure">Khi nhập những cấu hình có bảo mật TLS, mặc định sẽ bỏ qua xác minh chứng chỉ (allowInsecure: true).</string>
@@ -171,19 +171,19 @@
<string name="summary_pref_promotion">Quảng cáo, nhấn để biết thêm (Ủng hộ có thể được gỡ bỏ)</string>
<string name="title_pref_auto_update_subscription">Tự động cập nhật các gói đăng ký</string>
<string name="summary_pref_auto_update_subscription">Tự động cập nhật các gói đăng ký của bạn ở trong nền với khoảng thời gian cố định. Lưu ý rằng tùy thuộc vào thiết bị, tính năng này có thể không luôn hoạt động đúng như mong đợi.</string>
<string name="summary_pref_auto_update_subscription">Tự động cập nhật các gói đăng ký của bạn ở trong nền với khoảng thời gian cố định. Tùy thiết bị, tính năng này có thể không luôn hoạt động đúng như mong đợi.</string>
<string name="title_pref_auto_update_interval">Thời gian Cập nhật tự động (Phút, Giá trị tối thiểu 15)</string>
<string name="title_core_loglevel">Log level</string>
<string name="title_mode">Chế độ kết nối</string>
<string name="title_mode_help">Nhấn vào đây nếu bạn cần trợ giúp!</string>
<string name="title_language">Ngôn ngữ</string>
<string name="title_ui_settings">Cài đặt UI</string>
<string name="title_ui_settings">Cài đặt giao diện</string>
<string name="title_logcat">Logcat</string>
<string name="logcat_copy">Sao chép</string>
<string name="logcat_clear">Xoá</string>
<string name="title_service_restart">Kết nối lại v2rayNG</string>
<string name="title_service_restart">Khởi động lại dịch vụ lõi</string>
<string name="title_del_all_config">Xoá tất cả cấu hình</string>
<string name="title_del_duplicate_config">Xoá cấu hình trùng lặp</string>
<string name="title_del_invalid_config">Xoá cấu hình lỗi</string>
@@ -192,7 +192,7 @@
<string name="sub_setting_remarks">Tên gói đăng ký</string>
<string name="sub_setting_url">URL gói đăng ký</string>
<string name="sub_setting_enable">Sử dụng gói đăng ký này</string>
<string name="sub_auto_update">Bật tự động cập nhật</string>
<string name="sub_auto_update">Bật tự động cập nhật</string>
<string name="title_sub_update">Cập nhật các gói đăng ký</string>
<string name="title_ping_all_server">Ping tất cả máy chủ</string>
<string name="title_real_ping_all_server">Test HTTP tất cả máy chủ</string>
@@ -202,7 +202,7 @@
<string name="filter_config_all">Hiển thị tất cả các gói đăng ký</string>
<string name="title_del_duplicate_config_count">Xoá %d cấu hình trùng lặp</string>
<string name="tasker_start_service">Bắt đầu v2rayNG</string>
<string name="tasker_start_service">Khởi động v2rayNG</string>
<string name="tasker_setting_confirm">Xác nhận</string>
<string name="routing_settings_title">Cài đặt định tuyến</string>
@@ -210,7 +210,7 @@
<string name="routing_settings_save">Lưu lại</string>
<string name="routing_settings_delete">Xoá</string>
<string name="routing_settings_scan_replace">Quét QR và thay thế</string>
<string name="routing_settings_scan_append">Quét QR nối thêm</string>
<string name="routing_settings_scan_append">Quét QR và nối thêm</string>
<string name="routing_settings_default_rules">Tải xuống rules mặc định cho China</string>
<string name="connection_test_pending">Kiểm tra kết nối</string>
@@ -60,8 +60,8 @@
<string name="server_lab_encryption">加密方式(encryption)</string>
<string name="server_lab_flow">流控(flow)</string>
<string name="server_lab_reserved">Reserved(可选)</string>
<string name="server_lab_local_v4_address">本地 IPv4 地址(可选)</string>
<string name="server_lab_local_v6_address">本地 IPv6 地址(可选, 仅 IPv6 优先时使用)</string>
<string name="server_lab_local_address">本地地址(可选IPv4/IPv6,逗号隔开)</string>
<string name="server_lab_local_mtu">Mtu(可选, 默认1420)</string>
<string name="toast_success">成功</string>
<string name="toast_failure">失败</string>
<string name="toast_none_data">没有数据</string>
@@ -60,8 +60,8 @@
<string name="server_lab_encryption">加密 (encryption)</string>
<string name="server_lab_flow">流程 (flow)</string>
<string name="server_lab_reserved">Reserved (可選)</string>
<string name="server_lab_local_v4_address">本機 IPv4 位址(可選)</string>
<string name="server_lab_local_v6_address">本機 IPv6 位址(可選, 僅偏好 IPv6 時使用)</string>
<string name="server_lab_local_address">本機位址(可選IPv4/IPv6,逗號隔開)</string>
<string name="server_lab_local_mtu">Mtu(可選, 預設1420)</string>
<string name="toast_success">成功</string>
<string name="toast_failure">失敗</string>
<string name="toast_none_data">無資料</string>
+2 -2
View File
@@ -67,8 +67,8 @@
<string name="server_lab_spider_x" translatable="false">SpiderX</string>
<string name="server_lab_secret_key" translatable="false">SecretKey</string>
<string name="server_lab_reserved">Reserved(Optional)</string>
<string name="server_lab_local_v4_address">local address IPv4(Optional)</string>
<string name="server_lab_local_v6_address">local address IPv6(Optional, only used when prefer IPv6)</string>
<string name="server_lab_local_address">Local address (optional IPv4/IPv6, separated by commas)</string>
<string name="server_lab_local_mtu">Mtu(optional, default 1420)</string>
<string name="toast_success">Success</string>
<string name="toast_failure">Failure</string>
<string name="toast_none_data">There is nothing</string>