From dc31380cc29dd17332bbd43424794a505857ddfc Mon Sep 17 00:00:00 2001 From: yuhan6665 <1588741+yuhan6665@users.noreply.github.com> Date: Sat, 15 Aug 2020 19:37:59 -0400 Subject: [PATCH 1/3] Refactor EConfigType to be an enum Using enum will simplify logic and conveniently providing Int value and a String name --- .../main/kotlin/com/v2ray/ang/AppConfig.kt | 8 ------ .../kotlin/com/v2ray/ang/dto/EConfigType.kt | 12 +++++++++ .../kotlin/com/v2ray/ang/ui/MainActivity.kt | 3 ++- .../com/v2ray/ang/ui/MainRecyclerAdapter.kt | 26 +++++++----------- .../com/v2ray/ang/util/AngConfigManager.kt | 25 ++++++++--------- .../main/kotlin/com/v2ray/ang/util/Utils.kt | 15 ++--------- .../com/v2ray/ang/util/V2rayConfigUtil.kt | 27 ++++++++----------- 7 files changed, 49 insertions(+), 67 deletions(-) create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/EConfigType.kt 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 9f2278db..a196269f 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AppConfig.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AppConfig.kt @@ -50,12 +50,4 @@ object AppConfig { const val MSG_STATE_STOP = 4 const val MSG_STATE_STOP_SUCCESS = 41 const val MSG_STATE_RESTART = 5 - - object EConfigType { - val Vmess = 1 - val Custom = 2 - val Shadowsocks = 3 - val Socks = 4 - } - } \ No newline at end of file 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 new file mode 100644 index 00000000..0f98ff7b --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/EConfigType.kt @@ -0,0 +1,12 @@ +package com.v2ray.ang.dto + +enum class EConfigType(val value: Int) { + VMESS(1), + CUSTOM(2), + SHADOWSOCKS(3), + SOCKS(4); + + companion object { + fun fromInt(value: Int) = values().firstOrNull { it.value == value } + } +} \ No newline at end of file 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 a872572c..6d420339 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 @@ -27,6 +27,7 @@ import android.support.v4.view.GravityCompat import android.support.v7.app.ActionBarDrawerToggle import android.support.v7.widget.helper.ItemTouchHelper import android.util.Log +import com.v2ray.ang.dto.EConfigType //import com.v2ray.ang.InappBuyActivity import rx.Observable import rx.android.schedulers.AndroidSchedulers @@ -252,7 +253,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList adapter.updateConfigList() } for (k in 0 until configs.vmess.count()) { - if (configs.vmess[k].configType != AppConfig.EConfigType.Custom) { + if (configs.vmess[k].configType != EConfigType.CUSTOM.value) { testingJobs.add(GlobalScope.launch(Dispatchers.IO) { configs.vmess[k].testResult = Utils.tcping(configs.vmess[k].address, configs.vmess[k].port) val myJob = coroutineContext[Job] diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt index 5f9b6ac3..b557db80 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt @@ -8,6 +8,7 @@ import android.view.ViewGroup import com.v2ray.ang.AppConfig import com.v2ray.ang.R import com.v2ray.ang.dto.AngConfig +import com.v2ray.ang.dto.EConfigType import com.v2ray.ang.helper.ItemTouchHelperAdapter import com.v2ray.ang.helper.ItemTouchHelperViewHolder import com.v2ray.ang.util.AngConfigManager @@ -18,7 +19,6 @@ import org.jetbrains.anko.* import rx.Observable import rx.android.schedulers.AndroidSchedulers import java.util.concurrent.TimeUnit -import com.v2ray.ang.extension.defaultDPreference class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter() , ItemTouchHelperAdapter { @@ -49,7 +49,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter("position" to position, "isRunning" to !changeable) - } else if (configType == AppConfig.EConfigType.Custom) { + } else if (configType == EConfigType.CUSTOM) { mActivity.startActivity("position" to position, "isRunning" to !changeable) - } else if (configType == AppConfig.EConfigType.Shadowsocks) { + } else if (configType == EConfigType.SHADOWSOCKS) { mActivity.startActivity("position" to position, "isRunning" to !changeable) - } else if (configType == AppConfig.EConfigType.Socks) { + } else if (configType == EConfigType.SOCKS) { mActivity.startActivity("position" to position, "isRunning" to !changeable) } } 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 4eb6e9de..09358c0d 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.SS_PROTOCOL import com.v2ray.ang.AppConfig.VMESS_PROTOCOL import com.v2ray.ang.R import com.v2ray.ang.dto.AngConfig +import com.v2ray.ang.dto.EConfigType import com.v2ray.ang.dto.VmessQRCode import java.net.URI import java.net.URLDecoder @@ -65,7 +66,7 @@ object AngConfigManager { fun addServer(vmess: AngConfig.VmessBean, index: Int): Int { try { vmess.configVersion = 2 - vmess.configType = AppConfig.EConfigType.Vmess + vmess.configType = EConfigType.VMESS.value if (index >= 0) { //edit @@ -208,14 +209,14 @@ object AngConfigManager { return app.defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG, "") } - fun currConfigType(): Int { + fun currConfigType(): EConfigType? { if (angConfig.index < 0 || angConfig.vmess.count() <= 0 || angConfig.index > angConfig.vmess.count() - 1 ) { - return -1 + return null } - return angConfig.vmess[angConfig.index].configType + return EConfigType.fromInt(angConfig.vmess[angConfig.index].configType) } fun currConfigName(): String { @@ -275,7 +276,7 @@ object AngConfigManager { return R.string.toast_incorrect_protocol } - vmess.configType = AppConfig.EConfigType.Vmess + vmess.configType = EConfigType.VMESS.value vmess.security = "auto" vmess.network = "tcp" vmess.headerType = "none" @@ -478,7 +479,7 @@ object AngConfigManager { } val vmess = angConfig.vmess[index] - if (angConfig.vmess[index].configType == AppConfig.EConfigType.Vmess) { + if (angConfig.vmess[index].configType == EConfigType.VMESS.value) { val vmessQRCode = VmessQRCode() vmessQRCode.v = vmess.configVersion.toString() @@ -496,7 +497,7 @@ object AngConfigManager { val conf = VMESS_PROTOCOL + Utils.encode(json) return conf - } else if (angConfig.vmess[index].configType == AppConfig.EConfigType.Shadowsocks) { + } else if (angConfig.vmess[index].configType == EConfigType.SHADOWSOCKS.value) { val remark = "#" + Utils.urlEncode(vmess.remarks) val url = String.format("%s:%s@%s:%s", vmess.security, @@ -504,7 +505,7 @@ object AngConfigManager { vmess.address, vmess.port) return SS_PROTOCOL + Utils.encode(url) + remark - } else if (angConfig.vmess[index].configType == AppConfig.EConfigType.Socks) { + } else if (angConfig.vmess[index].configType == EConfigType.SOCKS.value) { val remark = "#" + Utils.urlEncode(vmess.remarks) val url = String.format("%s:%s", vmess.address, @@ -613,7 +614,7 @@ object AngConfigManager { //add val vmess = AngConfig.VmessBean() vmess.configVersion = 2 - vmess.configType = AppConfig.EConfigType.Custom + vmess.configType = EConfigType.CUSTOM.value vmess.guid = guid vmess.remarks = vmess.guid @@ -716,7 +717,7 @@ object AngConfigManager { fun addCustomServer(vmess: AngConfig.VmessBean, index: Int): Int { try { vmess.configVersion = 2 - vmess.configType = AppConfig.EConfigType.Custom + vmess.configType = EConfigType.CUSTOM.value if (index >= 0) { //edit @@ -741,7 +742,7 @@ object AngConfigManager { fun addShadowsocksServer(vmess: AngConfig.VmessBean, index: Int): Int { try { vmess.configVersion = 2 - vmess.configType = AppConfig.EConfigType.Shadowsocks + vmess.configType = EConfigType.SHADOWSOCKS.value if (index >= 0) { //edit @@ -766,7 +767,7 @@ object AngConfigManager { fun addSocksServer(vmess: AngConfig.VmessBean, index: Int): Int { try { vmess.configVersion = 2 - vmess.configType = AppConfig.EConfigType.Socks + vmess.configType = EConfigType.SOCKS.value if (index >= 0) { //edit diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/Utils.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/Utils.kt index e50a0075..1a403778 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/Utils.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/Utils.kt @@ -14,36 +14,25 @@ import kotlin.collections.HashMap import android.app.ActivityManager import android.content.ClipData import android.content.Intent -import android.content.res.AssetManager import android.net.Uri import android.os.SystemClock import android.text.TextUtils -import android.text.method.ScrollingMovementMethod import android.util.Log import android.util.Patterns -import android.view.View import android.webkit.URLUtil import com.v2ray.ang.AngApplication import com.v2ray.ang.AppConfig import com.v2ray.ang.R +import com.v2ray.ang.dto.EConfigType import com.v2ray.ang.extension.responseLength import com.v2ray.ang.extension.v2RayApplication import com.v2ray.ang.service.V2RayVpnService import com.v2ray.ang.ui.SettingsActivity -import kotlinx.android.synthetic.main.activity_logcat.* import kotlinx.coroutines.isActive import me.dozen.dpreference.DPreference import org.jetbrains.anko.toast -import org.jetbrains.anko.uiThread -import java.io.BufferedReader -import java.io.File import java.io.IOException -import java.io.InputStreamReader import java.net.* -import java.util.regex.Matcher -import java.util.regex.Pattern -import java.math.BigInteger -import java.util.concurrent.TimeUnit import libv2ray.Libv2ray import kotlin.coroutines.coroutineContext @@ -324,7 +313,7 @@ object Utils { if (AngConfigManager.genStoreV2rayConfig(-1)) { val configContent = AngConfigManager.currGeneratedV2rayConfig() val configType = AngConfigManager.currConfigType() - if (configType == AppConfig.EConfigType.Custom) { + if (configType == EConfigType.CUSTOM) { try { Libv2ray.testConfig(configContent) } catch (e: Exception) { 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 d3c97cc4..b00c8dc3 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 @@ -14,6 +14,7 @@ import org.json.JSONException import org.json.JSONObject import org.json.JSONArray import com.google.gson.JsonObject +import com.v2ray.ang.dto.EConfigType object V2rayConfigUtil { private val requestObj: JsonObject by lazy { @@ -40,13 +41,9 @@ object V2rayConfigUtil { // return result // } - if (vmess.configType == AppConfig.EConfigType.Vmess) { - result = getV2rayConfigType1(app, vmess) - } else if (vmess.configType == AppConfig.EConfigType.Custom) { + if (vmess.configType == EConfigType.CUSTOM.value) { result = getV2rayConfigType2(app, vmess) - } else if (vmess.configType == AppConfig.EConfigType.Shadowsocks) { - result = getV2rayConfigType1(app, vmess) - } else if (vmess.configType == AppConfig.EConfigType.Socks) { + } else { result = getV2rayConfigType1(app, vmess) } @@ -163,8 +160,12 @@ object V2rayConfigUtil { try { val outbound = v2rayConfig.outbounds[0] - when (vmess.configType) { - AppConfig.EConfigType.Vmess -> { + val configType = EConfigType.fromInt(vmess.configType) + if (configType != null) { + outbound.protocol = configType.name.toLowerCase() + } + when (configType) { + EConfigType.VMESS -> { outbound.settings?.servers = null val vnext = v2rayConfig.outbounds[0].settings?.vnext?.get(0) @@ -182,10 +183,8 @@ object V2rayConfigUtil { //远程服务器底层传输配置 outbound.streamSettings = boundStreamSettings(vmess) - - outbound.protocol = "vmess" } - AppConfig.EConfigType.Shadowsocks -> { + EConfigType.SHADOWSOCKS -> { outbound.settings?.vnext = null val server = outbound.settings?.servers?.get(0) @@ -198,10 +197,8 @@ object V2rayConfigUtil { //Mux outbound.mux?.enabled = false - - outbound.protocol = "shadowsocks" } - AppConfig.EConfigType.Socks -> { + EConfigType.SOCKS -> { outbound.settings?.vnext = null val server = outbound.settings?.servers?.get(0) @@ -210,8 +207,6 @@ object V2rayConfigUtil { //Mux outbound.mux?.enabled = false - - outbound.protocol = "socks" } else -> { } From 180b5efd9321dcbf6b750a7e26fbc1a827b4d5b6 Mon Sep 17 00:00:00 2001 From: yuhan6665 <1588741+yuhan6665@users.noreply.github.com> Date: Sat, 15 Aug 2020 19:37:59 -0400 Subject: [PATCH 2/3] Enable tcping test for custom config --- .../kotlin/com/v2ray/ang/dto/V2rayConfig.kt | 18 ++++++++++++++ .../kotlin/com/v2ray/ang/ui/MainActivity.kt | 24 ++++++++++++------- .../com/v2ray/ang/util/V2rayConfigUtil.kt | 18 ++++++++++++++ 3 files changed, 51 insertions(+), 9 deletions(-) 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 f8ef577f..57fd191f 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 @@ -108,6 +108,24 @@ data class V2rayConfig( } data class MuxBean(var enabled: Boolean) + + fun getServerAddress(): String? { + if (protocol.equals(EConfigType.VMESS.name.toLowerCase())) { + return settings?.vnext?.get(0)?.address + } else if (protocol.equals(EConfigType.SHADOWSOCKS.name.toLowerCase()) || protocol.equals(EConfigType.SOCKS.name.toLowerCase())) { + return settings?.servers?.get(0)?.address + } + return null + } + + fun getServerPort(): Int? { + if (protocol.equals(EConfigType.VMESS.name.toLowerCase())) { + return settings?.vnext?.get(0)?.port + } else if (protocol.equals(EConfigType.SHADOWSOCKS.name.toLowerCase()) || protocol.equals(EConfigType.SOCKS.name.toLowerCase())) { + return settings?.servers?.get(0)?.port + } + return null + } } //data class DnsBean(var servers: List) 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 6d420339..61565c80 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 @@ -253,16 +253,22 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList adapter.updateConfigList() } for (k in 0 until configs.vmess.count()) { - if (configs.vmess[k].configType != EConfigType.CUSTOM.value) { - testingJobs.add(GlobalScope.launch(Dispatchers.IO) { - configs.vmess[k].testResult = Utils.tcping(configs.vmess[k].address, configs.vmess[k].port) - val myJob = coroutineContext[Job] - launch(Dispatchers.Main) { - testingJobs.remove(myJob) - adapter.updateSelectedItem(k) - } - }) + var serverAddress = configs.vmess[k].address + var serverPort = configs.vmess[k].port + if (configs.vmess[k].configType == EConfigType.CUSTOM.value) { + val serverOutbound = V2rayConfigUtil.getCustomConfigServerOutbound(applicationContext, configs.vmess[k].guid) + ?: continue + serverAddress = serverOutbound.getServerAddress() ?: continue + serverPort = serverOutbound.getServerPort() ?: continue } + testingJobs.add(GlobalScope.launch(Dispatchers.IO) { + configs.vmess[k].testResult = Utils.tcping(serverAddress, serverPort) + val myJob = coroutineContext[Job] + launch(Dispatchers.Main) { + testingJobs.remove(myJob) + adapter.updateSelectedItem(k) + } + }) } true } 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 b00c8dc3..98e6506c 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 @@ -1,5 +1,6 @@ package com.v2ray.ang.util +import android.content.Context import android.text.TextUtils import android.util.Log import com.google.gson.Gson @@ -15,6 +16,7 @@ import org.json.JSONObject import org.json.JSONArray import com.google.gson.JsonObject import com.v2ray.ang.dto.EConfigType +import com.v2ray.ang.extension.defaultDPreference object V2rayConfigUtil { private val requestObj: JsonObject by lazy { @@ -60,6 +62,22 @@ object V2rayConfigUtil { } } + fun getCustomConfigServerOutbound(content: Context, guid: String): V2rayConfig.OutboundBean? { + val jsonConfig = content.defaultDPreference.getPrefString(AppConfig.ANG_CONFIG + guid, "") + if (TextUtils.isEmpty(jsonConfig)) { + return null + } + val v2rayConfig = Gson().fromJson(jsonConfig, V2rayConfig::class.java) ?: return null + for (outbound in v2rayConfig.outbounds) { + if (outbound.protocol.equals(EConfigType.VMESS.name.toLowerCase()) || + outbound.protocol.equals(EConfigType.SHADOWSOCKS.name.toLowerCase()) || + outbound.protocol.equals(EConfigType.SOCKS.name.toLowerCase())) { + return outbound + } + } + return null + } + /** * 生成v2ray的客户端配置文件 */ From b2437279dc2e1fa8f81b9c0debb189dfe6492a7c Mon Sep 17 00:00:00 2001 From: yuhan6665 <1588741+yuhan6665@users.noreply.github.com> Date: Sat, 15 Aug 2020 19:37:59 -0400 Subject: [PATCH 3/3] Update UI for custom config - add server address and port - add share button (full configuration) - update "Export all config to clipboard" text to indicate it will not include custom config --- .../com/v2ray/ang/ui/MainRecyclerAdapter.kt | 54 +++++++++++-------- .../src/main/res/values-zh-rCN/strings.xml | 2 +- .../src/main/res/values-zh-rTW/strings.xml | 2 +- V2rayNG/app/src/main/res/values/strings.xml | 2 +- 4 files changed, 36 insertions(+), 24 deletions(-) diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt index b557db80..cb675384 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt @@ -9,10 +9,12 @@ import com.v2ray.ang.AppConfig import com.v2ray.ang.R import com.v2ray.ang.dto.AngConfig import com.v2ray.ang.dto.EConfigType +import com.v2ray.ang.extension.defaultDPreference import com.v2ray.ang.helper.ItemTouchHelperAdapter import com.v2ray.ang.helper.ItemTouchHelperViewHolder import com.v2ray.ang.util.AngConfigManager import com.v2ray.ang.util.Utils +import com.v2ray.ang.util.V2rayConfigUtil import kotlinx.android.synthetic.main.item_qrcode.view.* import kotlinx.android.synthetic.main.item_recycler_main.view.* import org.jetbrains.anko.* @@ -67,31 +69,40 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter + mActivity.selector(null, shareOptions) { dialogInterface, i -> try { when (i) { 0 -> { - val iv = mActivity.layoutInflater.inflate(R.layout.item_qrcode, null) - iv.iv_qcode.setImageBitmap(AngConfigManager.share2QRCode(position)) + if (configType == EConfigType.CUSTOM) { + shareFullContent(position) + } else { + val iv = mActivity.layoutInflater.inflate(R.layout.item_qrcode, null) + iv.iv_qcode.setImageBitmap(AngConfigManager.share2QRCode(position)) - mActivity.alert { - customView { - linearLayout { - addView(iv) + mActivity.alert { + customView { + linearLayout { + addView(iv) + } } - } - }.show() + }.show() + } } 1 -> { if (AngConfigManager.share2Clipboard(position) == 0) { @@ -100,15 +111,8 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter { - if (AngConfigManager.shareFullContent2Clipboard(position) == 0) { - mActivity.toast(R.string.toast_success) - } else { - mActivity.toast(R.string.toast_failure) - } - } - else -> - mActivity.toast("else") + 2 -> shareFullContent(position) + else -> mActivity.toast("else") } } catch (e: Exception) { e.printStackTrace() @@ -168,6 +172,14 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter 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 3b0079fb..b08a711a 100644 --- a/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml +++ b/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml @@ -136,7 +136,7 @@ Logcat 复制 删除 - 导出全部配置至剪贴板 + 导出全部(非自定义)配置至剪贴板 订阅设置 备注 地址(url) 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 452be54d..d7310a34 100644 --- a/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml +++ b/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml @@ -138,7 +138,7 @@ Logcat 複製 刪除 - 匯出全部配置至剪貼簿 + 匯出全部(非自訂)配置至剪貼簿 訂閱設定 備註 位址(url) diff --git a/V2rayNG/app/src/main/res/values/strings.xml b/V2rayNG/app/src/main/res/values/strings.xml index edf83f0b..129d0264 100644 --- a/V2rayNG/app/src/main/res/values/strings.xml +++ b/V2rayNG/app/src/main/res/values/strings.xml @@ -138,7 +138,7 @@ Logcat Copy Delete - Export all config to clipboard + Export non-custom configs to clipboard Subscription setting remarks url