Merge pull request #1336 from yuhan6665/remove-alterid

Remove alterid
This commit is contained in:
2dust
2022-01-09 18:04:08 +08:00
committed by GitHub
17 changed files with 77 additions and 121 deletions

View File

@@ -54,7 +54,6 @@
"users": [
{
"id": "a3482e88-686a-4a58-8126-99c9df64b7bf",
"alterId": 64,
"security": "auto",
"level": 8
}

View File

@@ -89,7 +89,6 @@ data class V2rayConfig(
var users: List<UsersBean>) {
data class UsersBean(var id: String = "",
var alterId: Int? = null,
var security: String = DEFAULT_SECURITY,
var level: Int = DEFAULT_LEVEL,
var encryption: String = "",

View File

@@ -5,7 +5,7 @@ data class VmessQRCode(var v: String = "",
var add: String = "",
var port: String = "",
var id: String = "",
var aid: String = "",
var aid: String = "0",
var net: String = "",
var type: String = "",
var host: String = "",

View File

@@ -40,7 +40,6 @@ import libv2ray.Libv2ray
import me.drakeet.support.toast.ToastCompat
import rx.Observable
import rx.android.schedulers.AndroidSchedulers
import java.net.URL
import java.util.concurrent.TimeUnit
class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedListener {
@@ -109,16 +108,16 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
}
private fun setupViewModelObserver() {
mainViewModel.updateListAction.observe(this, {
mainViewModel.updateListAction.observe(this) {
val index = it ?: return@observe
if (index >= 0) {
adapter.notifyItemChanged(index)
} else {
adapter.notifyDataSetChanged()
}
})
mainViewModel.updateTestResultAction.observe(this, { binding.tvTestState.text = it })
mainViewModel.isRunning.observe(this, {
}
mainViewModel.updateTestResultAction.observe(this) { binding.tvTestState.text = it }
mainViewModel.isRunning.observe(this) {
val isRunning = it ?: return@observe
adapter.isRunning = isRunning
if (isRunning) {
@@ -131,7 +130,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
binding.layoutTest.isFocusable = false
}
hideCircle()
})
}
mainViewModel.startListenBroadcast()
}

View File

@@ -48,7 +48,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
override fun onBindViewHolder(holder: BaseViewHolder, position: Int) {
if (holder is MainViewHolder) {
val guid = mActivity.mainViewModel.serverList.getOrNull(position) ?: return
val config = mActivity.mainViewModel.serversCache.getOrElse(guid, { MmkvManager.decodeServerConfig(guid) })?: return
val config = mActivity.mainViewModel.serversCache.getOrElse(guid) { MmkvManager.decodeServerConfig(guid) } ?: return
val outbound = config.getProxyOutbound()
val aff = MmkvManager.decodeServerAffiliationInfo(guid)
@@ -69,13 +69,17 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
}
var shareOptions = share_method.asList()
if (config.configType == EConfigType.CUSTOM) {
holder.itemMainBinding.tvType.text = mActivity.getString(R.string.server_customize_config)
shareOptions = shareOptions.takeLast(1)
} else if (config.configType == EConfigType.VLESS) {
holder.itemMainBinding.tvType.text = config.configType.name
} else {
holder.itemMainBinding.tvType.text = config.configType.name.lowercase()
when (config.configType) {
EConfigType.CUSTOM -> {
holder.itemMainBinding.tvType.text = mActivity.getString(R.string.server_customize_config)
shareOptions = shareOptions.takeLast(1)
}
EConfigType.VLESS -> {
holder.itemMainBinding.tvType.text = config.configType.name
}
else -> {
holder.itemMainBinding.tvType.text = config.configType.name.lowercase()
}
}
holder.itemMainBinding.tvStatistics.text = "${outbound?.getServerAddress()} : ${outbound?.getServerPort()}"

View File

@@ -66,8 +66,8 @@ class PerAppProxyActivity : BaseActivity() {
one.isSelected = 0
}
}
val comparator = object : Comparator<AppInfo> {
override fun compare(p1: AppInfo, p2: AppInfo): Int = when {
val comparator = Comparator<AppInfo> { p1, p2 ->
when {
p1.isSelected > p2.isSelected -> -1
p1.isSelected == p2.isSelected -> 0
else -> 1

View File

@@ -128,7 +128,6 @@ class ServerActivity : BaseActivity() {
et_address.text = Utils.getEditable(outbound.getServerAddress().orEmpty())
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())
if (config.configType == EConfigType.SOCKS) {
et_security.text = Utils.getEditable(outbound.settings?.servers?.get(0)?.users?.get(0)?.user.orEmpty())
} else if (config.configType == EConfigType.VLESS) {
@@ -170,7 +169,6 @@ class ServerActivity : BaseActivity() {
et_address.text = null
et_port.text = Utils.getEditable(DEFAULT_PORT.toString())
et_id.text = null
et_alterId?.text = Utils.getEditable("0")
sp_security?.setSelection(0)
sp_network?.setSelection(0)
@@ -207,13 +205,6 @@ class ServerActivity : BaseActivity() {
toast(R.string.server_lab_id)
return false
}
et_alterId?.let {
val alterId = Utils.parseInt(et_alterId.text.toString())
if (alterId < 0) {
toast(R.string.server_lab_alterid)
return false
}
}
config.remarks = et_remarks.text.toString().trim()
config.outboundBean?.settings?.vnext?.get(0)?.let { vnext ->
@@ -237,13 +228,11 @@ class ServerActivity : BaseActivity() {
vnext.port = port
vnext.users[0].id = et_id.text.toString().trim()
if (config.configType == EConfigType.VMESS) {
vnext.users[0].alterId = Utils.parseInt(et_alterId.text.toString())
vnext.users[0].security = securitys[sp_security.selectedItemPosition]
} else if (config.configType == EConfigType.VLESS) {
vnext.users[0].encryption = et_security.text.toString().trim()
if (streamSecuritys[sp_stream_security.selectedItemPosition] == XTLS) {
// vnext.users[0].flow = if (flows[sp_flow.selectedItemPosition].isBlank()) V2rayConfig.DEFAULT_FLOW
// else flows[sp_flow.selectedItemPosition]
// vnext.users[0].flow = flows[sp_flow.selectedItemPosition].ifBlank { V2rayConfig.DEFAULT_FLOW }
} else {
vnext.users[0].flow = ""
}
@@ -272,7 +261,7 @@ class ServerActivity : BaseActivity() {
private fun saveStreamSettings(streamSetting: V2rayConfig.OutboundBean.StreamSettingsBean, config: ServerConfig) {
val network = if (sp_network != null) networks[sp_network.selectedItemPosition] else DEFAULT_NETWORK
val type = if (sp_header_type != null) transportTypes(network)[sp_header_type.selectedItemPosition] else "";
val type = if (sp_header_type != null) transportTypes(network)[sp_header_type.selectedItemPosition] else ""
val requestHost = if (et_request_host != null) et_request_host.text.toString().trim() else ""
val path = if (et_path != null) et_path.text.toString().trim() else ""
var sni = streamSetting.populateTransportSettings(

View File

@@ -17,7 +17,6 @@ import com.v2ray.ang.dto.V2rayConfig.Companion.DEFAULT_SECURITY
import com.v2ray.ang.dto.V2rayConfig.Companion.TLS
import com.v2ray.ang.util.MmkvManager.KEY_SELECTED_SERVER
import java.net.URI
import java.net.URLDecoder
import java.util.*
object AngConfigManager {
@@ -103,7 +102,6 @@ object AngConfigManager {
vnext.port = vmessBean.port
vnext.users[0].id = vmessBean.id
if (config.configType == EConfigType.VMESS) {
vnext.users[0].alterId = vmessBean.alterId
vnext.users[0].security = vmessBean.security
} else if (config.configType == EConfigType.VLESS) {
vnext.users[0].encryption = vmessBean.security
@@ -139,7 +137,7 @@ object AngConfigManager {
// vmessBean.allowInsecure.toBoolean()
// }
streamSetting.populateTlsSettings(vmessBean.streamSecurity, false,
sni)//if (vmessBean.sni.isNotBlank()) vmessBean.sni else sni)
sni)//vmessBean.sni.ifBlank { sni })
}
}
val key = MmkvManager.encodeServerConfig(vmessBean.guid, config)
@@ -196,7 +194,6 @@ object AngConfigManager {
if (TextUtils.isEmpty(vmessQRCode.add)
|| TextUtils.isEmpty(vmessQRCode.port)
|| TextUtils.isEmpty(vmessQRCode.id)
|| TextUtils.isEmpty(vmessQRCode.aid)
|| TextUtils.isEmpty(vmessQRCode.net)
) {
return R.string.toast_incorrect_protocol
@@ -208,7 +205,6 @@ object AngConfigManager {
vnext.port = Utils.parseInt(vmessQRCode.port)
vnext.users[0].id = vmessQRCode.id
vnext.users[0].encryption = DEFAULT_SECURITY
vnext.users[0].alterId = Utils.parseInt(vmessQRCode.aid)
}
val sni = streamSetting.populateTransportSettings(vmessQRCode.net, vmessQRCode.type, vmessQRCode.host,
vmessQRCode.path, vmessQRCode.path, vmessQRCode.host, vmessQRCode.path, vmessQRCode.type, vmessQRCode.path)
@@ -292,16 +288,14 @@ object AngConfigManager {
var sni = ""
uri.rawQuery?.let { rawQuery ->
val queryParam = rawQuery.split("&")
.map { it.split("=").let { (k, v) -> k to URLDecoder.decode(v, "utf-8")!! } }
.toMap()
.associate { it.split("=").let { (k, v) -> k to Utils.urlDecode(v) } }
sni = queryParam["sni"] ?: ""
}
config.outboundBean?.streamSettings?.populateTlsSettings(TLS, allowInsecure, sni)
} else if (str.startsWith(EConfigType.VLESS.protocolScheme)) {
val uri = URI(str)
val queryParam = uri.rawQuery.split("&")
.map { it.split("=").let { (k, v) -> k to URLDecoder.decode(v, "utf-8")!! } }
.toMap()
.associate { it.split("=").let { (k, v) -> k to Utils.urlDecode(v) } }
config = ServerConfig.create(EConfigType.VLESS)
val streamSetting = config.outboundBean?.streamSettings ?: return -1
config.remarks = uri.fragment ?: ""
@@ -339,14 +333,13 @@ object AngConfigManager {
return runCatching {
val uri = URI(uriString)
check(uri.scheme == "vmess")
val (_, protocol, tlsStr, uuid, alterId) =
Regex("(tcp|http|ws|kcp|quic|grpc)(\\+tls)?:([0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12})-([0-9]+)")
val (_, protocol, tlsStr, uuid) =
Regex("(tcp|http|ws|kcp|quic|grpc)(\\+tls)?:([0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12})")
.matchEntire(uri.userInfo)?.groupValues
?: error("parse user info fail.")
val tls = tlsStr.isNotBlank()
val queryParam = uri.rawQuery.split("&")
.map { it.split("=").let { (k, v) -> k to URLDecoder.decode(v, "utf-8")!! } }
.toMap()
.associate { it.split("=").let { (k, v) -> k to Utils.urlDecode(v) } }
val streamSetting = config.outboundBean?.streamSettings ?: return false
config.remarks = uri.fragment
@@ -355,7 +348,6 @@ object AngConfigManager {
vnext.port = uri.port
vnext.users[0].id = uuid
vnext.users[0].encryption = DEFAULT_SECURITY
vnext.users[0].alterId = alterId.toInt()
}
val sni = streamSetting.populateTransportSettings(protocol, queryParam["type"],
@@ -392,7 +384,6 @@ object AngConfigManager {
vnext.port = Utils.parseInt(arr22[1])
vnext.users[0].id = arr21[1]
vnext.users[0].encryption = arr21[0]
vnext.users[0].alterId = 0
}
return true
}
@@ -413,7 +404,6 @@ object AngConfigManager {
vmessQRCode.add = outbound.getServerAddress().orEmpty()
vmessQRCode.port = outbound.getServerPort().toString()
vmessQRCode.id = outbound.getPassword().orEmpty()
vmessQRCode.aid = outbound.settings?.vnext?.get(0)?.users?.get(0)?.alterId.toString()
vmessQRCode.net = streamSetting.network
vmessQRCode.tls = streamSetting.security
vmessQRCode.sni = streamSetting.tlsSettings?.serverName.orEmpty()
@@ -455,28 +445,24 @@ object AngConfigManager {
}
dicQuery["encryption"] = if (outbound.getSecurityEncryption().isNullOrEmpty()) "none"
else outbound.getSecurityEncryption().orEmpty()
dicQuery["security"] = if (streamSetting.security.isEmpty()) "none"
else streamSetting.security
dicQuery["security"] = streamSetting.security.ifEmpty { "none" }
(streamSetting.tlsSettings?: streamSetting.xtlsSettings)?.let { tlsSetting ->
if (!TextUtils.isEmpty(tlsSetting.serverName)) {
dicQuery["sni"] = tlsSetting.serverName
}
}
dicQuery["type"] = if (streamSetting.network.isEmpty()) V2rayConfig.DEFAULT_NETWORK
else streamSetting.network
dicQuery["type"] = streamSetting.network.ifEmpty { V2rayConfig.DEFAULT_NETWORK }
outbound.getTransportSettingDetails()?.let { transportDetails ->
when (streamSetting.network) {
"tcp" -> {
dicQuery["headerType"] = if (transportDetails[0].isEmpty()) "none"
else transportDetails[0]
dicQuery["headerType"] = transportDetails[0].ifEmpty { "none" }
if (!TextUtils.isEmpty(transportDetails[1])) {
dicQuery["host"] = Utils.urlEncode(transportDetails[1])
}
}
"kcp" -> {
dicQuery["headerType"] = if (transportDetails[0].isEmpty()) "none"
else transportDetails[0]
dicQuery["headerType"] = transportDetails[0].ifEmpty { "none" }
if (!TextUtils.isEmpty(transportDetails[2])) {
dicQuery["seed"] = Utils.urlEncode(transportDetails[2])
}
@@ -499,8 +485,7 @@ object AngConfigManager {
}
}
"quic" -> {
dicQuery["headerType"] = if (transportDetails[0].isEmpty()) "none"
else transportDetails[0]
dicQuery["headerType"] = transportDetails[0].ifEmpty { "none" }
dicQuery["quicSecurity"] = Utils.urlEncode(transportDetails[1])
dicQuery["key"] = Utils.urlEncode(transportDetails[2])
}

View File

@@ -42,11 +42,7 @@ object MmkvManager {
}
fun encodeServerConfig(guid: String, config: ServerConfig): String {
val key = if (guid.isBlank()) {
Utils.getUuid()
} else {
guid
}
val key = guid.ifBlank { Utils.getUuid() }
serverStorage?.encode(key, Gson().toJson(config))
val serverList= decodeServerList()
if (!serverList.contains(key)) {

View File

@@ -62,11 +62,11 @@ object Utils {
* parseInt
*/
fun parseInt(str: String): Int {
try {
return Integer.parseInt(str)
return try {
Integer.parseInt(str)
} catch (e: Exception) {
e.printStackTrace()
return 0
0
}
}
@@ -74,12 +74,12 @@ object Utils {
* get text from clipboard
*/
fun getClipboard(context: Context): String {
try {
return try {
val cmb = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
return cmb.primaryClip?.getItemAt(0)?.text.toString()
cmb.primaryClip?.getItemAt(0)?.text.toString()
} catch (e: Exception) {
e.printStackTrace()
return ""
""
}
}
@@ -126,11 +126,11 @@ object Utils {
* base64 encode
*/
fun encode(text: String): String {
try {
return Base64.encodeToString(text.toByteArray(charset("UTF-8")), Base64.NO_WRAP)
return try {
Base64.encodeToString(text.toByteArray(charset("UTF-8")), Base64.NO_WRAP)
} catch (e: Exception) {
e.printStackTrace()
return ""
""
}
}
@@ -172,12 +172,12 @@ object Utils {
fun createQRCode(text: String, size: Int = 800): Bitmap? {
try {
val hints = HashMap<EncodeHintType, String>()
hints.put(EncodeHintType.CHARACTER_SET, "utf-8")
hints[EncodeHintType.CHARACTER_SET] = "utf-8"
val bitMatrix = QRCodeWriter().encode(text,
BarcodeFormat.QR_CODE, size, size, hints)
val pixels = IntArray(size * size)
for (y in 0..size - 1) {
for (x in 0..size - 1) {
for (y in 0 until size) {
for (x in 0 until size) {
if (bitMatrix.get(x, y)) {
pixels[y * size + x] = 0xff000000.toInt()
} else {
@@ -222,7 +222,7 @@ object Utils {
}
// addr = addr.toLowerCase()
var octets = addr.split('.').toTypedArray()
val octets = addr.split('.').toTypedArray()
if (octets.size == 4) {
if(octets[3].indexOf(":") > 0) {
addr = addr.substring(0, addr.indexOf(":"))
@@ -302,29 +302,29 @@ object Utils {
* uuid
*/
fun getUuid(): String {
try {
return UUID.randomUUID().toString().replace("-", "")
return try {
UUID.randomUUID().toString().replace("-", "")
} catch (e: Exception) {
e.printStackTrace()
return ""
""
}
}
fun urlDecode(url: String): String {
try {
return URLDecoder.decode(url, "UTF-8")
return try {
URLDecoder.decode(url, "UTF-8")
} catch (e: Exception) {
e.printStackTrace()
return url
url
}
}
fun urlEncode(url: String): String {
try {
return URLEncoder.encode(url, "UTF-8")
return try {
URLEncoder.encode(url, "UTF-8")
} catch (e: Exception) {
e.printStackTrace()
return url
url
}
}
@@ -401,7 +401,7 @@ object Utils {
val allText = process.inputStream.bufferedReader().use { it.readText() }
if (allText.isNotBlank()) {
val tempInfo = allText.substring(allText.indexOf("min/avg/max/mdev") + 19)
val temps = tempInfo.split("/".toRegex()).dropLastWhile({ it.isEmpty() }).toTypedArray()
val temps = tempInfo.split("/".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
if (temps.count() > 0 && temps[0].length < 10) {
return temps[0].toFloat().toInt().toString() + "ms"
}

View File

@@ -93,7 +93,7 @@ object V2rayConfigUtil {
//val httpPort = Utils.parseInt(settingsStorage?.decodeString(AppConfig.PREF_HTTP_PORT) ?: AppConfig.PORT_HTTP)
v2rayConfig.inbounds.forEach { curInbound ->
if (!(settingsStorage?.decodeBool(AppConfig.PREF_PROXY_SHARING) ?: false)) {
if (settingsStorage?.decodeBool(AppConfig.PREF_PROXY_SHARING) != true) {
//bind all inbounds to localhost if the user requests
curInbound.listen = "127.0.0.1"
}
@@ -187,7 +187,7 @@ object V2rayConfigUtil {
val rulesIP = V2rayConfig.RoutingBean.RulesBean()
rulesIP.type = "field"
rulesIP.outboundTag = tag
rulesIP.ip = ArrayList<String>()
rulesIP.ip = ArrayList()
rulesIP.ip?.add("geoip:$code")
v2rayConfig.routing.rules.add(rulesIP)
}
@@ -197,7 +197,7 @@ object V2rayConfigUtil {
val rulesDomain = V2rayConfig.RoutingBean.RulesBean()
rulesDomain.type = "field"
rulesDomain.outboundTag = tag
rulesDomain.domain = ArrayList<String>()
rulesDomain.domain = ArrayList()
rulesDomain.domain?.add("geosite:$code")
v2rayConfig.routing.rules.add(rulesDomain)
}
@@ -214,13 +214,13 @@ object V2rayConfigUtil {
val rulesDomain = V2rayConfig.RoutingBean.RulesBean()
rulesDomain.type = "field"
rulesDomain.outboundTag = tag
rulesDomain.domain = ArrayList<String>()
rulesDomain.domain = ArrayList()
//IP
val rulesIP = V2rayConfig.RoutingBean.RulesBean()
rulesIP.type = "field"
rulesIP.outboundTag = tag
rulesIP.ip = ArrayList<String>()
rulesIP.ip = ArrayList()
userRule.split(",").map { it.trim() }.forEach {
if (Utils.isIpAddress(it) || it.startsWith("geoip:")) {
@@ -364,7 +364,7 @@ object V2rayConfigUtil {
}
// hardcode googleapi rule to fix play store problems
hosts.put("domain:googleapis.cn", "googleapis.com")
hosts["domain:googleapis.cn"] = "googleapis.com"
// DNS dns对象
v2rayConfig.dns = V2rayConfig.DnsBean(
@@ -393,14 +393,22 @@ object V2rayConfigUtil {
if (outbound.streamSettings?.network == DEFAULT_NETWORK
&& outbound.streamSettings?.tcpSettings?.header?.type == HTTP) {
val path = outbound.streamSettings?.tcpSettings?.header?.request?.path
val Host = outbound.streamSettings?.tcpSettings?.header?.request?.headers?.Host
val host = outbound.streamSettings?.tcpSettings?.header?.request?.headers?.Host
val requestString: String by lazy {
"""{"version":"1.1","method":"GET","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36","Mozilla/5.0 (iPhone; CPU iPhone OS 10_0_2 like Mac OS X) AppleWebKit/601.1 (KHTML, like Gecko) CriOS/53.0.2785.109 Mobile/14A456 Safari/601.1.46"],"Accept-Encoding":["gzip, deflate"],"Connection":["keep-alive"],"Pragma":"no-cache"}}"""
}
outbound.streamSettings?.tcpSettings?.header?.request = Gson().fromJson(requestString, V2rayConfig.OutboundBean.StreamSettingsBean.TcpSettingsBean.HeaderBean.RequestBean::class.java)
outbound.streamSettings?.tcpSettings?.header?.request?.path = path!!
outbound.streamSettings?.tcpSettings?.header?.request?.headers?.Host = Host!!
outbound.streamSettings?.tcpSettings?.header?.request = Gson().fromJson(
requestString,
V2rayConfig.OutboundBean.StreamSettingsBean.TcpSettingsBean.HeaderBean.RequestBean::class.java
)
outbound.streamSettings?.tcpSettings?.header?.request?.path =
if (path.isNullOrEmpty()) {
listOf("/")
} else {
path
}
outbound.streamSettings?.tcpSettings?.header?.request?.headers?.Host = host!!
}
} catch (e: Exception) {

View File

@@ -95,7 +95,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
getApplication<AngApplication>().toast(R.string.connection_test_testing)
for (guid in serverList) {
serversCache.getOrElse(guid, { MmkvManager.decodeServerConfig(guid) })?.getProxyOutbound()?.let { outbound ->
serversCache.getOrElse(guid) { MmkvManager.decodeServerConfig(guid) }?.getProxyOutbound()?.let { outbound ->
val serverAddress = outbound.getServerAddress()
val serverPort = outbound.getServerPort()
if (serverAddress != null && serverPort != null) {

View File

@@ -46,7 +46,7 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
AppConfig.PREF_BYPASS_APPS, -> {
settingsStorage?.encode(key, sharedPreferences.getBoolean(key, false))
}
AppConfig.PREF_SNIFFING_ENABLED, -> {
AppConfig.PREF_SNIFFING_ENABLED -> {
settingsStorage?.encode(key, sharedPreferences.getBoolean(key, true))
}
AppConfig.PREF_PER_APP_PROXY_SET -> {

View File

@@ -98,26 +98,6 @@
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_margin_top_height"
android:orientation="vertical">
<TextView
android:id="@+id/tv_alterId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/server_lab_alterid" />
<EditText
android:id="@+id/et_alterId"
android:layout_width="match_parent"
android:layout_height="@dimen/edit_height"
android:inputType="number" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@@ -38,7 +38,6 @@
<string name="server_lab_address">地址(address)</string>
<string name="server_lab_port">端口(port)</string>
<string name="server_lab_id">用户ID(id)</string>
<string name="server_lab_alterid">额外ID(alterId)</string>
<string name="server_lab_security">加密方式(security)</string>
<string name="server_lab_network">传输协议(network)</string>
<string name="server_lab_more_function">功能设置(不清楚则保持默认值)</string>

View File

@@ -38,7 +38,6 @@
<string name="server_lab_address">位址</string>
<string name="server_lab_port"></string>
<string name="server_lab_id">使用者識別碼</string>
<string name="server_lab_alterid">AlterId</string>
<string name="server_lab_security">安全性</string>
<string name="server_lab_network">網路</string>
<string name="server_lab_more_function">更多功能</string>

View File

@@ -38,7 +38,6 @@
<string name="server_lab_address">address</string>
<string name="server_lab_port">port</string>
<string name="server_lab_id">id</string>
<string name="server_lab_alterid">alterId</string>
<string name="server_lab_security">security</string>
<string name="server_lab_network">Network</string>
<string name="server_lab_more_function">more function</string>