From cfa709c651d5bb13b9da7f27e115f980b41990cd Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Wed, 25 Sep 2024 14:29:50 +0800 Subject: [PATCH] Add HYSTERIA2 for v2fly --- .../main/kotlin/com/v2ray/ang/AppConfig.kt | 2 + .../kotlin/com/v2ray/ang/dto/EConfigType.kt | 2 + .../kotlin/com/v2ray/ang/dto/ServerConfig.kt | 6 +- .../kotlin/com/v2ray/ang/dto/V2rayConfig.kt | 22 +++++- .../v2ray/ang/service/V2RayServiceManager.kt | 2 +- .../com/v2ray/ang/service/V2RayTestService.kt | 2 +- .../kotlin/com/v2ray/ang/ui/MainActivity.kt | 5 ++ .../kotlin/com/v2ray/ang/ui/ServerActivity.kt | 53 ++++++++------ .../com/v2ray/ang/util/AngConfigManager.kt | 4 + .../com/v2ray/ang/util/V2rayConfigUtil.kt | 1 + .../com/v2ray/ang/util/fmt/Hysteria2Fmt.kt | 73 +++++++++++++++++++ .../res/layout/activity_server_hysteria2.xml | 46 ++++++++++++ V2rayNG/app/src/main/res/menu/menu_main.xml | 4 + .../app/src/main/res/values-ar/strings.xml | 1 + .../app/src/main/res/values-bn/strings.xml | 1 + .../app/src/main/res/values-fa/strings.xml | 1 + .../app/src/main/res/values-ru/strings.xml | 1 + .../app/src/main/res/values-vi/strings.xml | 1 + .../src/main/res/values-zh-rCN/strings.xml | 1 + .../src/main/res/values-zh-rTW/strings.xml | 1 + V2rayNG/app/src/main/res/values/strings.xml | 1 + 21 files changed, 201 insertions(+), 29 deletions(-) create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/Hysteria2Fmt.kt create mode 100644 V2rayNG/app/src/main/res/layout/activity_server_hysteria2.xml diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AppConfig.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AppConfig.kt index 9b3ae860..bf5803a9 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AppConfig.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AppConfig.kt @@ -146,4 +146,6 @@ object AppConfig { const val VLESS = "vless://" const val TROJAN = "trojan://" const val WIREGUARD = "wireguard://" + const val TUIC = "tuic://" + const val HYSTERIA2 = "hysteria2://" } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/EConfigType.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/EConfigType.kt index 6ed15816..1dca4941 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/EConfigType.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/EConfigType.kt @@ -11,6 +11,8 @@ enum class EConfigType(val value: Int, val protocolScheme: String) { VLESS(5, AppConfig.VLESS), TROJAN(6, AppConfig.TROJAN), WIREGUARD(7, AppConfig.WIREGUARD), +// TUIC(8, AppConfig.TUIC), + HYSTERIA2(9, AppConfig.HYSTERIA2), HTTP(10, AppConfig.HTTP); companion object { diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ServerConfig.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ServerConfig.kt index a004f142..baa1fe48 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ServerConfig.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ServerConfig.kt @@ -16,7 +16,8 @@ data class ServerConfig( companion object { fun create(configType: EConfigType): ServerConfig { when (configType) { - EConfigType.VMESS, EConfigType.VLESS -> + EConfigType.VMESS, + EConfigType.VLESS -> return ServerConfig( configType = configType, outboundBean = V2rayConfig.OutboundBean( @@ -38,7 +39,8 @@ data class ServerConfig( EConfigType.SHADOWSOCKS, EConfigType.SOCKS, EConfigType.HTTP, - EConfigType.TROJAN -> + EConfigType.TROJAN, + EConfigType.HYSTERIA2 -> return ServerConfig( configType = configType, outboundBean = V2rayConfig.OutboundBean( diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/V2rayConfig.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/V2rayConfig.kt index dfbb1f26..323b7afc 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/V2rayConfig.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/V2rayConfig.kt @@ -147,8 +147,6 @@ data class V2rayConfig( val ivCheck: Boolean? = null, var users: List? = null ) { - - data class SocksUsersBean( var user: String = "", var pass: String = "", @@ -177,6 +175,7 @@ data class V2rayConfig( var quicSettings: QuicSettingBean? = null, var realitySettings: TlsSettingsBean? = null, var grpcSettings: GrpcSettingsBean? = null, + var hy2steriaSettings: Hy2steriaSettingsBean? = null, val dsSettings: Any? = null, var sockopt: SockoptBean? = null ) { @@ -295,6 +294,18 @@ data class V2rayConfig( var health_check_timeout: Int? = null ) + data class Hy2steriaSettingsBean( + var password: String? = null, + var use_udp_extension: Boolean? = true, + var congestion: Hy2CongestionBean? = null + ) { + data class Hy2CongestionBean( + var type: String? = "bbr", + var up_mbps: Int? = null, + var down_mbps: Int? = null, + ) + } + fun populateTransportSettings( transport: String, headerType: String?, host: String?, path: String?, seed: String?, quicSecurity: String?, key: String?, mode: String?, serviceName: String?, @@ -427,6 +438,7 @@ data class V2rayConfig( || protocol.equals(EConfigType.SOCKS.name, true) || protocol.equals(EConfigType.HTTP.name, true) || protocol.equals(EConfigType.TROJAN.name, true) + || protocol.equals(EConfigType.HYSTERIA2.name, true) ) { return settings?.servers?.get(0)?.address } else if (protocol.equals(EConfigType.WIREGUARD.name, true)) { @@ -444,6 +456,7 @@ data class V2rayConfig( || protocol.equals(EConfigType.SOCKS.name, true) || protocol.equals(EConfigType.HTTP.name, true) || protocol.equals(EConfigType.TROJAN.name, true) + || protocol.equals(EConfigType.HYSTERIA2.name, true) ) { return settings?.servers?.get(0)?.port } else if (protocol.equals(EConfigType.WIREGUARD.name, true)) { @@ -465,10 +478,12 @@ data class V2rayConfig( return settings?.vnext?.get(0)?.users?.get(0)?.id } else if (protocol.equals(EConfigType.SHADOWSOCKS.name, true) || protocol.equals(EConfigType.TROJAN.name, true) + || protocol.equals(EConfigType.HYSTERIA2.name, true) ) { return settings?.servers?.get(0)?.password } else if (protocol.equals(EConfigType.SOCKS.name, true) - || protocol.equals(EConfigType.HTTP.name, true)) { + || protocol.equals(EConfigType.HTTP.name, true) + ) { return settings?.servers?.get(0)?.users?.get(0)?.pass } else if (protocol.equals(EConfigType.WIREGUARD.name, true)) { return settings?.secretKey @@ -597,6 +612,7 @@ data class V2rayConfig( ) { data class RulesBean( + var type: String = "field", var ip: ArrayList? = null, var domain: ArrayList? = null, var outboundTag: String = "", diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayServiceManager.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayServiceManager.kt index 86a09153..2910d1d5 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayServiceManager.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayServiceManager.kt @@ -51,7 +51,7 @@ object V2RayServiceManager { set(value) { field = value Seq.setContext(value?.get()?.getService()?.applicationContext) - Libv2ray.initV2Env(Utils.userAssetPath(value?.get()?.getService()), Utils.getDeviceIdForXUDPBaseKey()) + Libv2ray.initV2Env(Utils.userAssetPath(value?.get()?.getService()))//, Utils.getDeviceIdForXUDPBaseKey()) } var currentConfig: ServerConfig? = null diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayTestService.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayTestService.kt index 6d6dc0aa..c48eeead 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayTestService.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayTestService.kt @@ -24,7 +24,7 @@ class V2RayTestService : Service() { override fun onCreate() { super.onCreate() Seq.setContext(this) - Libv2ray.initV2Env(Utils.userAssetPath(this), Utils.getDeviceIdForXUDPBaseKey()) + Libv2ray.initV2Env(Utils.userAssetPath(this)) //Utils.getDeviceIdForXUDPBaseKey()) } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt index 2c1a0cfe..ab31d8f0 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt @@ -292,6 +292,11 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList true } + R.id.import_manually_hysteria2 -> { + importManually(EConfigType.HYSTERIA2.value) + true + } + R.id.import_config_custom_clipboard -> { importConfigCustomClipboard() true diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ServerActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ServerActivity.kt index ec76abe5..63dde733 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ServerActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ServerActivity.kt @@ -134,6 +134,7 @@ class ServerActivity : BaseActivity() { EConfigType.VLESS -> setContentView(R.layout.activity_server_vless) EConfigType.TROJAN -> setContentView(R.layout.activity_server_trojan) EConfigType.WIREGUARD -> setContentView(R.layout.activity_server_wireguard) + EConfigType.HYSTERIA2 -> setContentView(R.layout.activity_server_hysteria2) } sp_network?.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { override fun onItemSelected( @@ -411,7 +412,10 @@ class ServerActivity : BaseActivity() { && config.configType != EConfigType.HTTP && TextUtils.isEmpty(et_id.text.toString()) ) { - if (config.configType == EConfigType.TROJAN || config.configType == EConfigType.SHADOWSOCKS) { + if (config.configType == EConfigType.TROJAN + || config.configType == EConfigType.SHADOWSOCKS + || config.configType == EConfigType.HYSTERIA2 + ) { toast(R.string.server_lab_id3) } else { toast(R.string.server_lab_id) @@ -443,8 +447,10 @@ class ServerActivity : BaseActivity() { wireguard?.peers?.get(0)?.let { _ -> savePeer(wireguard, port) } + config.outboundBean?.streamSettings?.let { - saveStreamSettings(it) + val sni = saveStreamSettings(it) + saveTls(it, sni) } if (config.subscriptionId.isEmpty() && !subscriptionId.isNullOrEmpty()) { config.subscriptionId = subscriptionId.orEmpty() @@ -493,7 +499,7 @@ class ServerActivity : BaseActivity() { socksUsersBean.pass = et_id.text.toString().trim() server.users = listOf(socksUsersBean) } - } else if (config.configType == EConfigType.TROJAN) { + } else if (config.configType == EConfigType.TROJAN || config.configType == EConfigType.HYSTERIA2) { server.password = et_id.text.toString().trim() } } @@ -515,21 +521,13 @@ class ServerActivity : BaseActivity() { wireguard.mtu = Utils.parseInt(et_local_mtu?.text.toString()) } - private fun saveStreamSettings(streamSetting: V2rayConfig.OutboundBean.StreamSettingsBean) { - val network = sp_network?.selectedItemPosition ?: return - val type = sp_header_type?.selectedItemPosition ?: return - val requestHost = et_request_host?.text?.toString()?.trim() ?: return - val path = et_path?.text?.toString()?.trim() ?: return - val sniField = et_sni?.text?.toString()?.trim() ?: return - val allowInsecureField = sp_allow_insecure?.selectedItemPosition ?: return - val streamSecurity = sp_stream_security?.selectedItemPosition ?: return - val utlsIndex = sp_stream_fingerprint?.selectedItemPosition ?: return - val alpnIndex = sp_stream_alpn?.selectedItemPosition ?: return - val publicKey = et_public_key?.text?.toString()?.trim() ?: return - val shortId = et_short_id?.text?.toString()?.trim() ?: return - val spiderX = et_spider_x?.text?.toString()?.trim() ?: return + private fun saveStreamSettings(streamSetting: V2rayConfig.OutboundBean.StreamSettingsBean): String? { + val network = sp_network?.selectedItemPosition ?: return null + val type = sp_header_type?.selectedItemPosition ?: return null + val requestHost = et_request_host?.text?.toString()?.trim() ?: return null + val path = et_path?.text?.toString()?.trim() ?: return null - var sni = streamSetting.populateTransportSettings( + val sni = streamSetting.populateTransportSettings( transport = networks[network], headerType = transportTypes(networks[network])[type], host = requestHost, @@ -541,10 +539,21 @@ class ServerActivity : BaseActivity() { serviceName = path, authority = requestHost, ) - if (sniField.isNotBlank()) { - sni = sniField - } - val allowInsecure = if (allowinsecures[allowInsecureField].isBlank()) { + + return sni + } + + private fun saveTls(streamSetting: V2rayConfig.OutboundBean.StreamSettingsBean, sni: String?) { + val streamSecurity = sp_stream_security?.selectedItemPosition ?: return + val sniField = et_sni?.text?.toString()?.trim() + val allowInsecureField = sp_allow_insecure?.selectedItemPosition + val utlsIndex = sp_stream_fingerprint?.selectedItemPosition ?: 0 + val alpnIndex = sp_stream_alpn?.selectedItemPosition ?: 0 + val publicKey = et_public_key?.text?.toString() + val shortId = et_short_id?.text?.toString() + val spiderX = et_spider_x?.text?.toString() + + val allowInsecure = if (allowInsecureField == null || allowinsecures[allowInsecureField].isBlank()) { settingsStorage?.decodeBool(PREF_ALLOW_INSECURE) ?: false } else { allowinsecures[allowInsecureField].toBoolean() @@ -553,7 +562,7 @@ class ServerActivity : BaseActivity() { streamSetting.populateTlsSettings( streamSecurity = streamSecuritys[streamSecurity], allowInsecure = allowInsecure, - sni = sni, + sni = sniField ?: sni ?: "", fingerprint = uTlsItems[utlsIndex], alpns = alpns[alpnIndex], publicKey = publicKey, diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt index 8408404c..6d48a0aa 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt @@ -14,6 +14,7 @@ import com.v2ray.ang.AppConfig import com.v2ray.ang.R import com.v2ray.ang.dto.* import com.v2ray.ang.util.MmkvManager.settingsStorage +import com.v2ray.ang.util.fmt.Hysteria2Fmt import com.v2ray.ang.util.fmt.ShadowsocksFmt import com.v2ray.ang.util.fmt.SocksFmt import com.v2ray.ang.util.fmt.TrojanFmt @@ -50,6 +51,8 @@ object AngConfigManager { VlessFmt.parseVless(str) } else if (str.startsWith(EConfigType.WIREGUARD.protocolScheme)) { WireguardFmt.parseWireguard(str) + } else if (str.startsWith(EConfigType.HYSTERIA2.protocolScheme)) { + Hysteria2Fmt.parseHysteria2(str) } else { null } @@ -92,6 +95,7 @@ object AngConfigManager { EConfigType.VLESS -> VlessFmt.toUri(config) EConfigType.TROJAN -> TrojanFmt.toUri(config) EConfigType.WIREGUARD -> WireguardFmt.toUri(config) + EConfigType.HYSTERIA2 -> Hysteria2Fmt.toUri(config) } } catch (e: Exception) { e.printStackTrace() diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/V2rayConfigUtil.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/V2rayConfigUtil.kt index 4764f403..eb668174 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/V2rayConfigUtil.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/V2rayConfigUtil.kt @@ -380,6 +380,7 @@ object V2rayConfigUtil { || protocol.equals(EConfigType.HTTP.name, true) || protocol.equals(EConfigType.TROJAN.name, true) || protocol.equals(EConfigType.WIREGUARD.name, true) + || protocol.equals(EConfigType.HYSTERIA2.name, true) ) { muxEnabled = false } else if (protocol.equals(EConfigType.VLESS.name, true) diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/Hysteria2Fmt.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/Hysteria2Fmt.kt new file mode 100644 index 00000000..01cbe35f --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/Hysteria2Fmt.kt @@ -0,0 +1,73 @@ +package com.v2ray.ang.util.fmt + +import android.text.TextUtils +import com.v2ray.ang.AppConfig +import com.v2ray.ang.dto.EConfigType +import com.v2ray.ang.dto.ServerConfig +import com.v2ray.ang.dto.V2rayConfig +import com.v2ray.ang.extension.idnHost +import com.v2ray.ang.util.MmkvManager.settingsStorage +import com.v2ray.ang.util.Utils +import java.net.URI + +object Hysteria2Fmt { + + fun parseHysteria2(str: String): ServerConfig { + var allowInsecure = settingsStorage?.decodeBool(AppConfig.PREF_ALLOW_INSECURE) ?: false + val config = ServerConfig.create(EConfigType.HYSTERIA2) + + val uri = URI(Utils.fixIllegalUrl(str)) + config.remarks = Utils.urlDecode(uri.fragment.orEmpty()) + + val queryParam = uri.rawQuery.split("&") + .associate { it.split("=").let { (k, v) -> k to Utils.urlDecode(v) } } + + config.outboundBean?.streamSettings?.populateTlsSettings( + V2rayConfig.TLS, + if ((queryParam["allowInsecure"].orEmpty()) == "1") true else allowInsecure, + queryParam["sni"] ?: uri.idnHost, + null, + queryParam["alpn"], + null, + null, + null + ) + + config.outboundBean?.settings?.servers?.get(0)?.let { server -> + server.address = uri.idnHost + server.port = uri.port + server.password = uri.userInfo + } + return config + } + + fun toUri(config: ServerConfig): String { + val outbound = config.getProxyOutbound() ?: return "" + val streamSetting = outbound.streamSettings ?: V2rayConfig.OutboundBean.StreamSettingsBean() + + val remark = "#" + Utils.urlEncode(config.remarks) + val dicQuery = HashMap() + dicQuery["security"] = streamSetting.security.ifEmpty { "none" } + streamSetting.tlsSettings?.let { tlsSetting -> + dicQuery["insecure"] = if (tlsSetting.allowInsecure) "1" else "0" + if (!TextUtils.isEmpty(tlsSetting.serverName)) { + dicQuery["sni"] = tlsSetting.serverName + } + if (!tlsSetting.alpn.isNullOrEmpty() && tlsSetting.alpn.isNotEmpty()) { + dicQuery["alpn"] = Utils.removeWhiteSpace(tlsSetting.alpn.joinToString()).orEmpty() + } + } + + val query = "?" + dicQuery.toList().joinToString( + separator = "&", + transform = { it.first + "=" + it.second }) + + val url = String.format( + "%s@%s:%s", + outbound.getPassword(), + Utils.getIpv6Address(outbound.getServerAddress()), + outbound.getServerPort() + ) + return url + query + remark + } +} \ No newline at end of file diff --git a/V2rayNG/app/src/main/res/layout/activity_server_hysteria2.xml b/V2rayNG/app/src/main/res/layout/activity_server_hysteria2.xml new file mode 100644 index 00000000..6907c848 --- /dev/null +++ b/V2rayNG/app/src/main/res/layout/activity_server_hysteria2.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/V2rayNG/app/src/main/res/menu/menu_main.xml b/V2rayNG/app/src/main/res/menu/menu_main.xml index c701223b..1144010e 100644 --- a/V2rayNG/app/src/main/res/menu/menu_main.xml +++ b/V2rayNG/app/src/main/res/menu/menu_main.xml @@ -48,6 +48,10 @@ android:id="@+id/import_manually_wireguard" android:title="@string/menu_item_import_config_manually_wireguard" app:showAsAction="never" /> + Type manually[HTTP] الكتابة يدويًا [Trojan] الكتابة يدويًا [Wireguard] + Type manually[Hysteria2] تكوين مخصص استيراد تكوين مخصص من الحافظة استيراد تكوين مخصص من الجهاز diff --git a/V2rayNG/app/src/main/res/values-bn/strings.xml b/V2rayNG/app/src/main/res/values-bn/strings.xml index 5c06b789..f4836dc5 100644 --- a/V2rayNG/app/src/main/res/values-bn/strings.xml +++ b/V2rayNG/app/src/main/res/values-bn/strings.xml @@ -32,6 +32,7 @@ Type manually[HTTP] ম্যানুয়ালি টাইপ করুন [Trojan] ম্যানুয়ালি টাইপ করুন [Wireguard] + Type manually[Hysteria2] কাস্টম কনফিগারেশন ক্লিপবোর্ড থেকে কাস্টম কনফিগারেশন আমদানি করুন স্থানীয়ভাবে কাস্টম কনফিগারেশন আমদানি করুন diff --git a/V2rayNG/app/src/main/res/values-fa/strings.xml b/V2rayNG/app/src/main/res/values-fa/strings.xml index 1cbccca4..dea8ed51 100644 --- a/V2rayNG/app/src/main/res/values-fa/strings.xml +++ b/V2rayNG/app/src/main/res/values-fa/strings.xml @@ -31,6 +31,7 @@ Type manually[HTTP] تایپ دستی[Trojan] [Wireguard]تایپ دستی + Type manually[Hysteria2] کانفیگ سفارشی کانفیگ سفارشی را از کلیپ‌بورد وارد کنید کانفیگ سفارشی را به صورت محلی وارد کنید diff --git a/V2rayNG/app/src/main/res/values-ru/strings.xml b/V2rayNG/app/src/main/res/values-ru/strings.xml index 0e7e03ce..56f6e131 100644 --- a/V2rayNG/app/src/main/res/values-ru/strings.xml +++ b/V2rayNG/app/src/main/res/values-ru/strings.xml @@ -31,6 +31,7 @@ Ручной ввод HTTP Ручной ввод Trojan Ручной ввод WireGuard + Type manually[Hysteria2] Другой профиль Импорт из буфера обмена Импорт из файла diff --git a/V2rayNG/app/src/main/res/values-vi/strings.xml b/V2rayNG/app/src/main/res/values-vi/strings.xml index 271e5d5c..5621af00 100644 --- a/V2rayNG/app/src/main/res/values-vi/strings.xml +++ b/V2rayNG/app/src/main/res/values-vi/strings.xml @@ -31,6 +31,7 @@ Type manually[HTTP] Nhập thủ công [Trojan] Nhập thủ công [WireGuard] + Type manually[Hysteria2] Nâng cao / Cấu hình tùy chỉnh Nhập cấu hình tùy chỉnh từ Clipboard Nhập cấu hình tùy chỉnh từ Tệp diff --git a/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml b/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml index 4349ce6a..d1e31ba7 100644 --- a/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml +++ b/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml @@ -31,6 +31,7 @@ 手动输入[HTTP] 手动输入[Trojan] 手动输入[Wireguard] + 手动输入[Hysteria2] 自定义配置 从剪贴板导入自定义配置 从本地导入自定义配置 diff --git a/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml b/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml index d69b0e3b..f95323e2 100644 --- a/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml +++ b/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml @@ -31,6 +31,7 @@ 手動鍵入 [HTTP] 手動鍵入 [Trojan] 手動鍵入 [Wireguard] + 手動鍵入 [Hysteria2] 自訂配置 從剪貼簿匯入自訂配置 從本地匯入自訂配置 diff --git a/V2rayNG/app/src/main/res/values/strings.xml b/V2rayNG/app/src/main/res/values/strings.xml index dd4494ad..764cdbd8 100644 --- a/V2rayNG/app/src/main/res/values/strings.xml +++ b/V2rayNG/app/src/main/res/values/strings.xml @@ -32,6 +32,7 @@ Type manually[HTTP] Type manually[Trojan] Type manually[Wireguard] + Type manually[Hysteria2] Custom config Import custom config from Clipboard Import custom config from locally