Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a95f280102 | |||
| df8da05f32 | |||
| 635581719b | |||
| 77d5e203e8 | |||
| 370d002b25 |
@@ -11,8 +11,8 @@ android {
|
||||
applicationId = "com.v2ray.ang"
|
||||
minSdk = 21
|
||||
targetSdk = 34
|
||||
versionCode = 598
|
||||
versionName = "1.9.5"
|
||||
versionCode = 599
|
||||
versionName = "1.9.6"
|
||||
multiDexEnabled = true
|
||||
splits {
|
||||
abi {
|
||||
|
||||
@@ -27,13 +27,6 @@
|
||||
"geosite:category-ads-all"
|
||||
]
|
||||
},
|
||||
{
|
||||
"remarks": "绕过局域网域名",
|
||||
"outboundTag": "direct",
|
||||
"domain": [
|
||||
"geosite:private"
|
||||
]
|
||||
},
|
||||
{
|
||||
"remarks": "绕过局域网IP",
|
||||
"outboundTag": "direct",
|
||||
@@ -41,6 +34,13 @@
|
||||
"geoip:private"
|
||||
]
|
||||
},
|
||||
{
|
||||
"remarks": "绕过局域网域名",
|
||||
"outboundTag": "direct",
|
||||
"domain": [
|
||||
"geosite:private"
|
||||
]
|
||||
},
|
||||
{
|
||||
"remarks": "代理GFW",
|
||||
"outboundTag": "proxy",
|
||||
|
||||
@@ -12,13 +12,6 @@
|
||||
"geosite:category-ads-all"
|
||||
]
|
||||
},
|
||||
{
|
||||
"remarks": "绕过局域网域名",
|
||||
"outboundTag": "direct",
|
||||
"domain": [
|
||||
"geosite:private"
|
||||
]
|
||||
},
|
||||
{
|
||||
"remarks": "绕过局域网IP",
|
||||
"outboundTag": "direct",
|
||||
@@ -26,6 +19,13 @@
|
||||
"geoip:private"
|
||||
]
|
||||
},
|
||||
{
|
||||
"remarks": "绕过局域网域名",
|
||||
"outboundTag": "direct",
|
||||
"domain": [
|
||||
"geosite:private"
|
||||
]
|
||||
},
|
||||
{
|
||||
"remarks": "最终代理",
|
||||
"port": "0-65535",
|
||||
|
||||
@@ -20,13 +20,6 @@
|
||||
"geosite:category-ads-all"
|
||||
]
|
||||
},
|
||||
{
|
||||
"remarks": "绕过局域网域名",
|
||||
"outboundTag": "direct",
|
||||
"domain": [
|
||||
"geosite:private"
|
||||
]
|
||||
},
|
||||
{
|
||||
"remarks": "绕过局域网IP",
|
||||
"outboundTag": "direct",
|
||||
@@ -34,6 +27,13 @@
|
||||
"geoip:private"
|
||||
]
|
||||
},
|
||||
{
|
||||
"remarks": "绕过局域网域名",
|
||||
"outboundTag": "direct",
|
||||
"domain": [
|
||||
"geosite:private"
|
||||
]
|
||||
},
|
||||
{
|
||||
"remarks": "绕过中国域名",
|
||||
"outboundTag": "direct",
|
||||
|
||||
@@ -105,7 +105,10 @@ object AppConfig {
|
||||
const val DNS_PROXY = "1.1.1.1"
|
||||
const val DNS_DIRECT = "223.5.5.5"
|
||||
const val DNS_VPN = "1.1.1.1"
|
||||
|
||||
const val GEOSITE_PRIVATE = "geosite:private"
|
||||
const val GEOSITE_CN = "geosite:cn"
|
||||
const val GEOIP_PRIVATE = "geoip:private"
|
||||
const val GEOIP_CN = "geoip:cn"
|
||||
|
||||
/** Ports and addresses for various services. */
|
||||
const val PORT_LOCAL_DNS = "10853"
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.v2ray.ang.service
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import com.v2ray.ang.AppConfig.ANG_PACKAGE
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class ProcessService {
|
||||
private val TAG = ANG_PACKAGE
|
||||
private lateinit var process: Process
|
||||
|
||||
fun runProcess(context: Context, cmd: MutableList<String>) {
|
||||
Log.d(TAG, cmd.toString())
|
||||
|
||||
try {
|
||||
val proBuilder = ProcessBuilder(cmd)
|
||||
proBuilder.redirectErrorStream(true)
|
||||
process = proBuilder
|
||||
.directory(context.filesDir)
|
||||
.start()
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
Thread.sleep(50L)
|
||||
Log.d(TAG, "runProcess check")
|
||||
process.waitFor()
|
||||
Log.d(TAG, "runProcess exited")
|
||||
}
|
||||
Log.d(TAG, process.toString())
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.d(TAG, e.toString())
|
||||
}
|
||||
}
|
||||
|
||||
fun stopProcess() {
|
||||
try {
|
||||
Log.d(TAG, "runProcess destroy")
|
||||
process?.destroy()
|
||||
} catch (e: Exception) {
|
||||
Log.d(TAG, e.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package com.v2ray.ang.service
|
||||
import android.app.Service
|
||||
import android.content.Intent
|
||||
import android.os.IBinder
|
||||
import android.util.Log
|
||||
import com.v2ray.ang.AppConfig.MSG_MEASURE_CONFIG
|
||||
import com.v2ray.ang.AppConfig.MSG_MEASURE_CONFIG_CANCEL
|
||||
import com.v2ray.ang.AppConfig.MSG_MEASURE_CONFIG_SUCCESS
|
||||
@@ -59,17 +58,8 @@ class V2RayTestService : Service() {
|
||||
|
||||
val server = MmkvManager.decodeServerConfig(guid) ?: return retFailure
|
||||
if (server.getProxyOutbound()?.protocol?.equals(EConfigType.HYSTERIA2.name, true) == true) {
|
||||
val socksPort = Utils.findFreePort(listOf(0))
|
||||
PluginUtil.runPlugin(this, server, "0:${socksPort}")
|
||||
Thread.sleep(1000L)
|
||||
|
||||
var delay = SpeedtestUtil.testConnection(this, socksPort)
|
||||
if (delay.first < 0) {
|
||||
Thread.sleep(10L)
|
||||
delay = SpeedtestUtil.testConnection(this, socksPort)
|
||||
}
|
||||
PluginUtil.stopPlugin()
|
||||
return delay.first
|
||||
val delay = PluginUtil.realPingHy2(this, server)
|
||||
return delay
|
||||
} else {
|
||||
val config = V2rayConfigUtil.getV2rayConfig(this, guid)
|
||||
if (!config.status) {
|
||||
|
||||
@@ -3,45 +3,33 @@ package com.v2ray.ang.util
|
||||
import android.content.Context
|
||||
import android.os.SystemClock
|
||||
import android.util.Log
|
||||
|
||||
import com.v2ray.ang.AppConfig.ANG_PACKAGE
|
||||
import com.v2ray.ang.dto.EConfigType
|
||||
import com.v2ray.ang.dto.ServerConfig
|
||||
import com.v2ray.ang.service.ProcessService
|
||||
import com.v2ray.ang.util.fmt.Hysteria2Fmt
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
|
||||
object PluginUtil {
|
||||
//private const val HYSTERIA2 = "hysteria2-plugin"
|
||||
private const val HYSTERIA2 = "libhysteria2.so"
|
||||
private const val packageName = ANG_PACKAGE
|
||||
private lateinit var process: Process
|
||||
private const val TAG = ANG_PACKAGE
|
||||
private lateinit var procService: ProcessService
|
||||
|
||||
// fun initPlugin(name: String): PluginManager.InitResult {
|
||||
// return PluginManager.init(name)!!
|
||||
// }
|
||||
|
||||
fun runPlugin(context: Context, config: ServerConfig?, domainPort: String?) {
|
||||
Log.d(packageName, "runPlugin")
|
||||
Log.d(TAG, "runPlugin")
|
||||
|
||||
val outbound = config?.getProxyOutbound() ?: return
|
||||
if (outbound.protocol.equals(EConfigType.HYSTERIA2.name, true)) {
|
||||
Log.d(packageName, "runPlugin $HYSTERIA2")
|
||||
val configFile = genConfigHy2(context, config, domainPort) ?: return
|
||||
val cmd = genCmdHy2(context, configFile)
|
||||
|
||||
val socksPort = domainPort?.split(":")?.last()
|
||||
.let { if (it.isNullOrEmpty()) return else it.toInt() }
|
||||
val hy2Config = Hysteria2Fmt.toNativeConfig(config, socksPort) ?: return
|
||||
|
||||
val configFile = File(context.noBackupFilesDir, "hy2_${SystemClock.elapsedRealtime()}.json")
|
||||
Log.d(packageName, "runPlugin ${configFile.absolutePath}")
|
||||
|
||||
configFile.parentFile?.mkdirs()
|
||||
configFile.writeText(JsonUtil.toJson(hy2Config))
|
||||
Log.d(packageName, JsonUtil.toJson(hy2Config))
|
||||
|
||||
runHy2(context, configFile)
|
||||
procService = ProcessService()
|
||||
procService.runProcess(context, cmd)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,8 +37,46 @@ object PluginUtil {
|
||||
stopHy2()
|
||||
}
|
||||
|
||||
private fun runHy2(context: Context, configFile: File) {
|
||||
val cmd = mutableListOf(
|
||||
fun realPingHy2(context: Context, config: ServerConfig?): Long {
|
||||
Log.d(TAG, "realPingHy2")
|
||||
val retFailure = -1L
|
||||
|
||||
val outbound = config?.getProxyOutbound() ?: return retFailure
|
||||
if (outbound.protocol.equals(EConfigType.HYSTERIA2.name, true)) {
|
||||
val socksPort = Utils.findFreePort(listOf(0))
|
||||
val configFile = genConfigHy2(context, config, "0:${socksPort}") ?: return retFailure
|
||||
val cmd = genCmdHy2(context, configFile)
|
||||
|
||||
val proc = ProcessService()
|
||||
proc.runProcess(context, cmd)
|
||||
Thread.sleep(1000L)
|
||||
val delay = SpeedtestUtil.testConnection(context, socksPort)
|
||||
proc.stopProcess()
|
||||
|
||||
return delay.first
|
||||
}
|
||||
return retFailure
|
||||
}
|
||||
|
||||
private fun genConfigHy2(context: Context, config: ServerConfig, domainPort: String?): File? {
|
||||
Log.d(TAG, "runPlugin $HYSTERIA2")
|
||||
|
||||
val socksPort = domainPort?.split(":")?.last()
|
||||
.let { if (it.isNullOrEmpty()) return null else it.toInt() }
|
||||
val hy2Config = Hysteria2Fmt.toNativeConfig(config, socksPort) ?: return null
|
||||
|
||||
val configFile = File(context.noBackupFilesDir, "hy2_${SystemClock.elapsedRealtime()}.json")
|
||||
Log.d(TAG, "runPlugin ${configFile.absolutePath}")
|
||||
|
||||
configFile.parentFile?.mkdirs()
|
||||
configFile.writeText(JsonUtil.toJson(hy2Config))
|
||||
Log.d(TAG, JsonUtil.toJson(hy2Config))
|
||||
|
||||
return configFile
|
||||
}
|
||||
|
||||
private fun genCmdHy2(context: Context, configFile: File): MutableList<String> {
|
||||
return mutableListOf(
|
||||
File(context.applicationInfo.nativeLibraryDir, HYSTERIA2).absolutePath,
|
||||
//initPlugin(HYSTERIA2).path,
|
||||
"--disable-update-check",
|
||||
@@ -60,34 +86,14 @@ object PluginUtil {
|
||||
"warn",
|
||||
"client"
|
||||
)
|
||||
Log.d(packageName, cmd.toString())
|
||||
|
||||
try {
|
||||
val proBuilder = ProcessBuilder(cmd)
|
||||
proBuilder.redirectErrorStream(true)
|
||||
process = proBuilder
|
||||
.directory(context.filesDir)
|
||||
.start()
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
Thread.sleep(500L)
|
||||
Log.d(packageName, "$HYSTERIA2 check")
|
||||
process.waitFor()
|
||||
Log.d(packageName, "$HYSTERIA2 exited")
|
||||
}
|
||||
Log.d(packageName, process.toString())
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.d(packageName, e.toString())
|
||||
}
|
||||
}
|
||||
|
||||
private fun stopHy2() {
|
||||
try {
|
||||
Log.d(packageName, "$HYSTERIA2 destroy")
|
||||
process?.destroy()
|
||||
Log.d(TAG, "$HYSTERIA2 destroy")
|
||||
procService?.stopProcess()
|
||||
} catch (e: Exception) {
|
||||
Log.d(packageName, e.toString())
|
||||
Log.d(TAG, e.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import android.content.Context
|
||||
import android.text.TextUtils
|
||||
|
||||
import com.v2ray.ang.AppConfig
|
||||
import com.v2ray.ang.AppConfig.GEOIP_PRIVATE
|
||||
import com.v2ray.ang.AppConfig.GEOSITE_PRIVATE
|
||||
import com.v2ray.ang.dto.RulesetItem
|
||||
import com.v2ray.ang.dto.ServerConfig
|
||||
import com.v2ray.ang.util.MmkvManager.decodeProfileConfig
|
||||
@@ -109,7 +111,11 @@ object SettingsManager {
|
||||
|
||||
fun routingRulesetsBypassLan(): Boolean {
|
||||
val rulesetItems = MmkvManager.decodeRoutingRulesets()
|
||||
val exist = rulesetItems?.any { it.enabled && it.domain?.contains(":private") == true }
|
||||
val exist = rulesetItems?.any {
|
||||
it.enabled
|
||||
&& (it.domain?.contains(GEOSITE_PRIVATE) == true
|
||||
|| it.ip?.contains(GEOIP_PRIVATE) == true)
|
||||
}
|
||||
return exist == true
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,10 @@ import android.util.Log
|
||||
|
||||
import com.v2ray.ang.AppConfig
|
||||
import com.v2ray.ang.AppConfig.ANG_PACKAGE
|
||||
import com.v2ray.ang.AppConfig.GEOIP_CN
|
||||
import com.v2ray.ang.AppConfig.GEOSITE_CN
|
||||
import com.v2ray.ang.AppConfig.LOOPBACK
|
||||
import com.v2ray.ang.AppConfig.GEOSITE_PRIVATE
|
||||
import com.v2ray.ang.AppConfig.PROTOCOL_FREEDOM
|
||||
import com.v2ray.ang.AppConfig.TAG_BLOCKED
|
||||
import com.v2ray.ang.AppConfig.TAG_DIRECT
|
||||
@@ -222,7 +225,9 @@ object V2rayConfigUtil {
|
||||
rulesetItems?.forEach { key ->
|
||||
if (key != null && key.enabled && key.outboundTag == tag && !key.domain.isNullOrEmpty()) {
|
||||
key.domain?.forEach {
|
||||
if (it.startsWith("geosite:") || it.startsWith("domain:")) {
|
||||
if (it != GEOSITE_PRIVATE
|
||||
&& (it.startsWith("geosite:") || it.startsWith("domain:"))
|
||||
) {
|
||||
domain.add(it)
|
||||
}
|
||||
}
|
||||
@@ -235,7 +240,7 @@ object V2rayConfigUtil {
|
||||
private fun customLocalDns(v2rayConfig: V2rayConfig): Boolean {
|
||||
try {
|
||||
if (settingsStorage?.decodeBool(AppConfig.PREF_FAKE_DNS_ENABLED) == true) {
|
||||
val geositeCn = arrayListOf("geosite:cn")
|
||||
val geositeCn = arrayListOf(GEOSITE_CN)
|
||||
val proxyDomain = userRule2Domain(TAG_PROXY)
|
||||
val directDomain = userRule2Domain(TAG_DIRECT)
|
||||
// fakedns with all domains to make it always top priority
|
||||
@@ -326,8 +331,8 @@ object V2rayConfigUtil {
|
||||
// domestic DNS
|
||||
val domesticDns = Utils.getDomesticDnsServers()
|
||||
val directDomain = userRule2Domain(TAG_DIRECT)
|
||||
val isCnRoutingMode = directDomain.contains("geosite:cn")
|
||||
val geoipCn = arrayListOf("geoip:cn")
|
||||
val isCnRoutingMode = directDomain.contains(GEOSITE_CN)
|
||||
val geoipCn = arrayListOf(GEOIP_CN)
|
||||
if (directDomain.size > 0) {
|
||||
servers.add(
|
||||
V2rayConfig.DnsBean.ServersBean(
|
||||
|
||||
@@ -110,7 +110,7 @@
|
||||
<string name="msg_file_not_found">Файл не найден</string>
|
||||
<string name="msg_remark_is_duplicate">Название уже существует</string>
|
||||
<string name="toast_action_not_allowed">Это действие запрещено</string>
|
||||
<string name="server_obfs_password">Obfs password</string>
|
||||
<string name="server_obfs_password">Пароль obfs</string>
|
||||
|
||||
<!-- PerAppProxyActivity -->
|
||||
<string name="msg_dialog_progress">Загрузка…</string>
|
||||
@@ -253,15 +253,15 @@
|
||||
|
||||
<string name="routing_settings_domain_strategy">Доменная стратегия</string>
|
||||
<string name="routing_settings_title">Маршрутизация</string>
|
||||
<string name="routing_settings_tips">Введите требуемые значения через запятую</string>
|
||||
<string name="routing_settings_tips">Введите требуемые домены/IP через запятую</string>
|
||||
<string name="routing_settings_save">Сохранить</string>
|
||||
<string name="routing_settings_delete">Очистить</string>
|
||||
<string name="routing_settings_rule_title">Настройка правил маршрутизации</string>
|
||||
<string name="routing_settings_add_rule">Добавить правило</string>
|
||||
<string name="routing_settings_import_rulesets">Импорт правил</string>
|
||||
<string name="routing_settings_import_rulesets_tip">Существующие правила будут удалены. Продолжить?</string>
|
||||
<string name="routing_settings_import_rulesets_from_clipboard">Import ruleset from clipboard</string>
|
||||
<string name="routing_settings_export_rulesets_to_clipboard">Export ruleset to clipboard</string>
|
||||
<string name="routing_settings_import_rulesets_from_clipboard">Импорт правил из буфера обмена</string>
|
||||
<string name="routing_settings_export_rulesets_to_clipboard">Экспорт правил в буфер обмена</string>
|
||||
<string name="routing_settings_locked">Постоянное (сохранится при импорте правил)</string>
|
||||
<string name="routing_settings_domain">Домен</string>
|
||||
<string name="routing_settings_ip">IP</string>
|
||||
|
||||
@@ -18,12 +18,12 @@
|
||||
<string name="toast_services_failure">啟動服務失敗</string>
|
||||
|
||||
<!--ServerActivity-->
|
||||
<string name="title_server">配置檔案</string>
|
||||
<string name="menu_item_add_config">新增配置</string>
|
||||
<string name="menu_item_save_config">儲存配置</string>
|
||||
<string name="menu_item_del_config">刪除配置</string>
|
||||
<string name="menu_item_import_config_qrcode">從 QR Code 匯入配置</string>
|
||||
<string name="menu_item_import_config_clipboard">從剪貼簿匯入配置</string>
|
||||
<string name="title_server">設定檔</string>
|
||||
<string name="menu_item_add_config">新增設定</string>
|
||||
<string name="menu_item_save_config">儲存設定</string>
|
||||
<string name="menu_item_del_config">刪除設定</string>
|
||||
<string name="menu_item_import_config_qrcode">從 QR Code 匯入設定</string>
|
||||
<string name="menu_item_import_config_clipboard">從剪貼簿匯入設定</string>
|
||||
<string name="menu_item_import_config_manually_vmess">手動鍵入 [VMess]</string>
|
||||
<string name="menu_item_import_config_manually_vless">手動鍵入 [VLESS]</string>
|
||||
<string name="menu_item_import_config_manually_ss">手動鍵入 [Shadowsocks]</string>
|
||||
@@ -32,11 +32,11 @@
|
||||
<string name="menu_item_import_config_manually_trojan">手動鍵入 [Trojan]</string>
|
||||
<string name="menu_item_import_config_manually_wireguard">手動鍵入 [Wireguard]</string>
|
||||
<string name="menu_item_import_config_manually_hysteria2">手動鍵入 [Hysteria2]</string>
|
||||
<string name="menu_item_import_config_custom">自訂配置</string>
|
||||
<string name="menu_item_import_config_custom_clipboard">從剪貼簿匯入自訂配置</string>
|
||||
<string name="menu_item_import_config_custom_local">從本地匯入自訂配置</string>
|
||||
<string name="menu_item_import_config_custom_url">從 URL 匯入自訂配置</string>
|
||||
<string name="menu_item_import_config_custom_url_scan">掃描 URL 匯入自訂配置</string>
|
||||
<string name="menu_item_import_config_custom">自訂設定</string>
|
||||
<string name="menu_item_import_config_custom_clipboard">從剪貼簿匯入自訂設定</string>
|
||||
<string name="menu_item_import_config_custom_local">從本地匯入自訂設定</string>
|
||||
<string name="menu_item_import_config_custom_url">從 URL 匯入自訂設定</string>
|
||||
<string name="menu_item_import_config_custom_url_scan">掃描 URL 匯入自訂設定</string>
|
||||
<string name="del_config_comfirm">確定刪除?</string>
|
||||
<string name="del_invalid_config_comfirm">刪除前請先測試!確認刪除?</string>
|
||||
<string name="server_lab_remarks">備註</string>
|
||||
@@ -90,15 +90,15 @@
|
||||
<string name="toast_none_data">無資料</string>
|
||||
<string name="toast_incorrect_protocol">通訊協定不正確</string>
|
||||
<string name="toast_decoding_failed">解碼失敗</string>
|
||||
<string name="title_file_chooser">選取一個配置檔</string>
|
||||
<string name="title_file_chooser">選取一個設定檔</string>
|
||||
<string name="toast_require_file_manager">請安裝檔案總管。</string>
|
||||
<string name="server_customize_config">自訂配置</string>
|
||||
<string name="toast_config_file_invalid">無效配置</string>
|
||||
<string name="server_customize_config">自訂設定</string>
|
||||
<string name="toast_config_file_invalid">無效設定</string>
|
||||
<string name="server_lab_content">內容</string>
|
||||
<string name="toast_none_data_clipboard">剪貼簿內無資料</string>
|
||||
<string name="toast_invalid_url">URL 無效</string>
|
||||
<string name="server_lab_need_inbound">確保 inbounds port 和設定中的一致</string>
|
||||
<string name="toast_malformed_josn">配置格式不正確</string>
|
||||
<string name="toast_malformed_josn">設定格式不正確</string>
|
||||
<string name="server_lab_request_host6">Host(SNI)(可選)</string>
|
||||
<string name="toast_asset_copy_failed">失敗,請使用檔案總管</string>
|
||||
<string name="menu_item_add_file">新增檔案</string>
|
||||
@@ -188,8 +188,8 @@
|
||||
<string name="title_pref_local_dns_port">本機 DNS 埠</string>
|
||||
<string name="summary_pref_local_dns_port">本機 DNS 埠</string>
|
||||
|
||||
<string name="title_pref_confirm_remove">刪除配置檔案確認</string>
|
||||
<string name="summary_pref_confirm_remove">刪除配置檔案是否需要用戶二次確認</string>
|
||||
<string name="title_pref_confirm_remove">刪除設定檔確認</string>
|
||||
<string name="summary_pref_confirm_remove">刪除設定檔是否需要用戶二次確認</string>
|
||||
|
||||
<string name="title_pref_start_scan_immediate">立即啟動掃碼</string>
|
||||
<string name="summary_pref_start_scan_immediate">啟動時立即打開相機掃描,否則可在工具欄選擇掃碼或選照片</string>
|
||||
@@ -202,10 +202,10 @@
|
||||
<string name="title_about">關於</string>
|
||||
<string name="title_source_code">原始碼</string>
|
||||
<string name="title_tg_channel">Telegram 頻道</string>
|
||||
<string name="title_configuration_backup">備份配置</string>
|
||||
<string name="title_configuration_backup">備份設定</string>
|
||||
<string name="summary_configuration_backup">儲存位置: [%s], 卸載App或清除儲存後備份將被清除</string>
|
||||
<string name="title_configuration_restore">還原配置</string>
|
||||
<string name="title_configuration_share">分享配置</string>
|
||||
<string name="title_configuration_restore">還原設定</string>
|
||||
<string name="title_configuration_share">分享設定</string>
|
||||
|
||||
<string name="title_pref_promotion">推廣</string>
|
||||
<string name="summary_pref_promotion">一些推廣,輕觸以檢視 (捐贈可去除)</string>
|
||||
@@ -225,10 +225,10 @@
|
||||
<string name="logcat_copy">複製</string>
|
||||
<string name="logcat_clear">清除</string>
|
||||
<string name="title_service_restart">重啟服務</string>
|
||||
<string name="title_del_all_config">刪除目前群組配置</string>
|
||||
<string name="title_del_duplicate_config">刪除目前群組重複配置</string>
|
||||
<string name="title_del_invalid_config">刪除目前群組無效配置</string>
|
||||
<string name="title_export_all">匯出目前群組配置至剪貼簿</string>
|
||||
<string name="title_del_all_config">刪除目前群組設定</string>
|
||||
<string name="title_del_duplicate_config">刪除目前群組重複設定</string>
|
||||
<string name="title_del_invalid_config">刪除目前群組無效設定</string>
|
||||
<string name="title_export_all">匯出目前群組設定至剪貼簿</string>
|
||||
<string name="title_sub_setting">訂閱分組設定</string>
|
||||
<string name="sub_setting_remarks">備註</string>
|
||||
<string name="sub_setting_url">可選位址(url)</string>
|
||||
@@ -239,11 +239,11 @@
|
||||
<string name="sub_setting_next_profile">落地代理別名</string>
|
||||
<string name="sub_setting_pre_profile_tip">请确保别名存在并唯一</string>
|
||||
<string name="title_sub_update">更新目前群組訂閱</string>
|
||||
<string name="title_ping_all_server">偵測目前群組配置 Tcping</string>
|
||||
<string name="title_real_ping_all_server">偵測目前群組配置真延遲</string>
|
||||
<string name="title_ping_all_server">偵測目前群組設定 Tcping</string>
|
||||
<string name="title_real_ping_all_server">偵測目前群組設定真延遲</string>
|
||||
<string name="title_user_asset_setting">Geo 資源檔案</string>
|
||||
<string name="title_sort_by_test_results">依偵測結果排序</string>
|
||||
<string name="title_filter_config">過濾配置</string>
|
||||
<string name="title_filter_config">過濾設定</string>
|
||||
<string name="filter_config_all">所有分組</string>
|
||||
<string name="title_del_duplicate_config_count">Delete %d duplicate configurations</string>
|
||||
|
||||
@@ -285,7 +285,7 @@
|
||||
<string-array name="share_method">
|
||||
<item>QR Code</item>
|
||||
<item>匯出至剪貼簿</item>
|
||||
<item>匯出完整配置至剪貼簿</item>
|
||||
<item>匯出完整設定至剪貼簿</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="share_sub_method">
|
||||
|
||||
Reference in New Issue
Block a user