Compare commits

...

15 Commits

Author SHA1 Message Date
2dust 1672494ee9 1.9.45 2025-04-01 10:26:59 +08:00
2dust bbbbc72d22 Update AndroidLibXrayLite 2025-04-01 10:26:45 +08:00
2dust 1e7f49b756 up 1.9.44 2025-03-30 19:14:55 +08:00
2dust ac4c0f7ee1 Optimize and improve Log 2025-03-30 19:05:35 +08:00
2dust 6cc91b1a89 Optimize and improve log
Use Log.e() instead of e.printStackTrace()
2025-03-30 17:40:36 +08:00
2dust 45facff41d Optimize and improve Utils 2025-03-30 16:28:14 +08:00
2dust ee703e6c95 Remove ads rules from default routing rules 2025-03-30 11:18:39 +08:00
hhhkkmk 87213c34a6 Revert "Optimization (#4426)" (#4437)
This reverts commit d111328541.
2025-03-29 18:06:09 +08:00
solokot 73a7c76183 Update Russian translation (#4435) 2025-03-29 18:05:43 +08:00
Pk-web6936 ed5282f2b3 Update dependencies (#4432)
* Update libs.versions.toml

Update agp

* Update validate-fastlane-supply-metadata

Update validate-fastlane-supply-metadata
2025-03-29 10:47:09 +08:00
Pk-web6936 390c657047 Switch to Loyalsoldier's v2ray-rules-dat (#4431)
https://github.com/2dust/AndroidLibXrayLite/pull/132/files
2025-03-29 10:46:49 +08:00
Pk-web6936 7071072862 Fix Arabic Language (#4430)
* Fix Arabic Language

* Fix Arabic Language

Fix Arabic Language
2025-03-29 10:46:29 +08:00
Pk-web6936 d111328541 Optimization (#4426)
* Optimization

Add security flags to CFLAGS and LDFLAGS. Use local variables instead of global variables. Clean up and simplify the script.

* Optimizition

Add security flags to CFLAGS and LDFLAGS. Simplify and organize the file. Makefile

* Optimization

Add security flags to CFLAGS and LDFLAGS. Use local variables instead of global variables. Clean up and simplify the script.

* Optimization
2025-03-29 10:44:21 +08:00
Pk-web6936 76cb2aaf46 Update Persian translate (#4424) 2025-03-29 10:44:03 +08:00
2dust 7ff1397163 Optimize the test true delay function
When testing, remove unnecessary configurations such as dns and routing to reduce resource usage, except for custom configurations.
2025-03-29 10:43:25 +08:00
52 changed files with 426 additions and 320 deletions
+1 -1
View File
@@ -13,4 +13,4 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Validate Fastlane Supply Metadata
uses: ashutoshgngwr/validate-fastlane-supply-metadata@v2.0.0
uses: ashutoshgngwr/validate-fastlane-supply-metadata@v2.1.0
+1 -1
View File
@@ -21,7 +21,7 @@ A V2Ray client for Android, support [Xray core](https://github.com/XTLS/Xray-cor
#### Geoip and Geosite
- geoip.dat and geosite.dat files are in `Android/data/com.v2ray.ang/files/assets` (path may differ on some Android device)
- download feature will get enhanced version in this [repo](https://github.com/Loyalsoldier/v2ray-rules-dat) (Note it need a working proxy)
- latest official [domain list](https://github.com/v2fly/domain-list-community) and [ip list](https://github.com/v2fly/geoip) can be imported manually
- latest official [domain list](https://github.com/Loyalsoldier/v2ray-rules-dat) and [ip list](https://github.com/Loyalsoldier/geoip) can be imported manually
- possible to use third party dat file in the same folder, like [h2y](https://guide.v2fly.org/routing/sitedata.html#%E5%A4%96%E7%BD%AE%E7%9A%84%E5%9F%9F%E5%90%8D%E6%96%87%E4%BB%B6)
### More in our [wiki](https://github.com/2dust/v2rayNG/wiki)
+2 -2
View File
@@ -12,8 +12,8 @@ android {
applicationId = "com.v2ray.ang"
minSdk = 21
targetSdk = 35
versionCode = 643
versionName = "1.9.43"
versionCode = 645
versionName = "1.9.45"
multiDexEnabled = true
val abiFilterList = (properties["ABI_FILTERS"] as? String)?.split(';')
@@ -20,13 +20,6 @@
"port": "443",
"network": "udp"
},
{
"remarks": "阻断广告",
"outboundTag": "block",
"domain": [
"geosite:category-ads-all"
]
},
{
"remarks": "绕过局域网IP",
"outboundTag": "direct",
@@ -5,13 +5,6 @@
"port": "443",
"network": "udp"
},
{
"remarks": "阻断广告",
"outboundTag": "block",
"domain": [
"geosite:category-ads-all"
]
},
{
"remarks": "绕过局域网IP",
"outboundTag": "direct",
@@ -13,13 +13,6 @@
"port": "443",
"network": "udp"
},
{
"remarks": "阻断广告",
"outboundTag": "block",
"domain": [
"geosite:category-ads-all"
]
},
{
"remarks": "绕过局域网IP",
"outboundTag": "direct",
@@ -5,13 +5,6 @@
"port": "443",
"network": "udp"
},
{
"remarks": "Block ads and trackers",
"outboundTag": "block",
"domain": [
"geosite:category-ads-all"
]
},
{
"remarks": "Direct LAN IP",
"outboundTag": "direct",
@@ -5,6 +5,7 @@ object AppConfig {
/** The application's package name. */
const val ANG_PACKAGE = BuildConfig.APPLICATION_ID
const val TAG = BuildConfig.APPLICATION_ID
/** Directory names used in the app's file system. */
const val DIR_ASSETS = "assets"
@@ -8,6 +8,7 @@ enum class Language(val code: String) {
VIETNAMESE("vi"),
RUSSIAN("ru"),
PERSIAN("fa"),
ARABIC("ar"),
BANGLA("bn"),
BAKHTIARI("bqi-rIR");
@@ -22,7 +22,7 @@ data class V2rayConfig(
var policy: PolicyBean?,
val inbounds: ArrayList<InboundBean>,
var outbounds: ArrayList<OutboundBean>,
var dns: DnsBean,
var dns: DnsBean? = null,
val routing: RoutingBean,
val api: Any? = null,
val transport: Any? = null,
@@ -1,5 +1,7 @@
package com.v2ray.ang.fmt
import android.util.Log
import com.v2ray.ang.AppConfig
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.dto.NetworkType
import com.v2ray.ang.dto.ProfileItem
@@ -82,7 +84,7 @@ object ShadowsocksFmt : FmtBase() {
config.remarks =
Utils.urlDecode(result.substring(indexSplit + 1, result.length))
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to decode remarks in SS legacy URL", e)
}
result = result.substring(0, indexSplit)
@@ -33,7 +33,7 @@ object VmessFmt : FmtBase() {
var result = str.replace(EConfigType.VMESS.protocolScheme, "")
result = Utils.decode(result)
if (TextUtils.isEmpty(result)) {
Log.d(AppConfig.ANG_PACKAGE, "R.string.toast_decoding_failed")
Log.w(AppConfig.TAG, "Toast decoding failed")
return null
}
val vmessQRCode = JsonUtil.fromJson(result, VmessQRCode::class.java)
@@ -43,7 +43,7 @@ object VmessFmt : FmtBase() {
|| TextUtils.isEmpty(vmessQRCode.id)
|| TextUtils.isEmpty(vmessQRCode.net)
) {
Log.d(AppConfig.ANG_PACKAGE, "R.string.toast_incorrect_protocol")
Log.w(AppConfig.TAG, "Toast incorrect protocol")
return null
}
@@ -42,7 +42,7 @@ object AngConfigManager {
Utils.setClipboard(context, conf)
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to share config to clipboard", e)
return -1
}
return 0
@@ -71,7 +71,7 @@ object AngConfigManager {
}
return sb.lines().count()
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to share non-custom configs to clipboard", e)
return -1
}
}
@@ -91,7 +91,7 @@ object AngConfigManager {
return QRCodeDecoder.createQRCode(conf)
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to share config as QR code", e)
return null
}
}
@@ -120,7 +120,7 @@ object AngConfigManager {
return -1
}
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to share full content to clipboard", e)
return -1
}
return 0
@@ -148,7 +148,7 @@ object AngConfigManager {
EConfigType.HYSTERIA2 -> Hysteria2Fmt.toUri(config)
}
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to share config for GUID: $guid", e)
return ""
}
}
@@ -203,7 +203,7 @@ object AngConfigManager {
}
return count
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to parse batch subscription", e)
}
return 0
}
@@ -251,7 +251,7 @@ object AngConfigManager {
}
return count
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to parse batch config", e)
}
return 0
}
@@ -287,7 +287,7 @@ object AngConfigManager {
return count
}
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to parse custom config server JSON array", e)
}
try {
@@ -298,7 +298,7 @@ object AngConfigManager {
MmkvManager.encodeServerRaw(key, server)
return 1
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to parse custom config server as single config", e)
}
return 0
} else if (server.startsWith("[Interface]") && server.contains("[Peer]")) {
@@ -308,7 +308,7 @@ object AngConfigManager {
MmkvManager.encodeServerRaw(key, server)
return 1
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to parse WireGuard config file", e)
}
return 0
} else {
@@ -372,7 +372,7 @@ object AngConfigManager {
MmkvManager.setSelectServer(guid)
}
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to parse config", e)
return -1
}
return 0
@@ -390,7 +390,7 @@ object AngConfigManager {
count += updateConfigViaSub(it)
}
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to update config via all subscriptions", e)
return 0
}
return count
@@ -417,21 +417,20 @@ object AngConfigManager {
if (!Utils.isValidUrl(url)) {
return 0
}
Log.d(AppConfig.ANG_PACKAGE, url)
Log.i(AppConfig.TAG, url)
var configText = try {
val httpPort = SettingsManager.getHttpPort()
HttpUtil.getUrlContentWithUserAgent(url, 15000, httpPort)
} catch (e: Exception) {
Log.e(AppConfig.ANG_PACKAGE, "Update subscription: proxy not ready or other error, try……")
//e.printStackTrace()
Log.e(AppConfig.ANG_PACKAGE, "Update subscription: proxy not ready or other error", e)
""
}
if (configText.isEmpty()) {
configText = try {
HttpUtil.getUrlContentWithUserAgent(url)
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to get URL content with user agent", e)
""
}
}
@@ -440,7 +439,7 @@ object AngConfigManager {
}
return parseConfigViaSub(configText, it.first, false)
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to update config via subscription", e)
return 0
}
}
@@ -3,7 +3,6 @@ package com.v2ray.ang.handler
import android.util.Log
import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig
import com.v2ray.ang.AppConfig.ANG_PACKAGE
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.dto.NetworkType
import com.v2ray.ang.dto.ProfileItem
@@ -26,7 +25,7 @@ object MigrateManager {
return false
}
val serverList = serverStorage.allKeys() ?: return false
Log.d(ANG_PACKAGE, "migrateServerConfig2Profile-" + serverList.count())
Log.i(AppConfig.TAG, "migrateServerConfig2Profile-" + serverList.count())
for (guid in serverList) {
var configOld = decodeServerConfigOld(guid) ?: continue
@@ -43,9 +42,9 @@ object MigrateManager {
//check and remove old
decodeServerConfig(guid) ?: continue
serverStorage.remove(guid)
Log.d(ANG_PACKAGE, "migrateServerConfig2Profile-" + config.remarks)
Log.i(AppConfig.TAG, "migrateServerConfig2Profile-" + config.remarks)
}
Log.d(ANG_PACKAGE, "migrateServerConfig2Profile-end")
Log.i(AppConfig.TAG, "migrateServerConfig2Profile-end")
return true
}
@@ -571,7 +571,7 @@ object MmkvManager {
* @param startOnBoot Whether to start on boot.
*/
fun encodeStartOnBoot(startOnBoot: Boolean) {
MmkvManager.encodeSettings(PREF_IS_BOOTED, startOnBoot)
encodeSettings(PREF_IS_BOOTED, startOnBoot)
}
/**
@@ -84,7 +84,7 @@ object SettingsManager {
resetRoutingRulesetsCommon(rulesetList)
return true
} catch (e: Exception) {
e.printStackTrace()
Log.e(ANG_PACKAGE, "Failed to reset routing rulesets", e)
return false
}
}
@@ -167,11 +167,11 @@ object SettingsManager {
}
val guid = MmkvManager.getSelectServer() ?: return false
val config = MmkvManager.decodeServerConfig(guid) ?: return false
val config = decodeServerConfig(guid) ?: return false
if (config.configType == EConfigType.CUSTOM) {
val raw = MmkvManager.decodeServerRaw(guid) ?: return false
val v2rayConfig = JsonUtil.fromJson(raw, V2rayConfig::class.java)
val exist = v2rayConfig.routing.rules.filter { it.outboundTag == TAG_DIRECT }?.any {
val exist = v2rayConfig.routing.rules.filter { it.outboundTag == TAG_DIRECT }.any {
it.domain?.contains(GEOSITE_PRIVATE) == true || it.ip?.contains(GEOIP_PRIVATE) == true
}
return exist == true
@@ -265,10 +265,7 @@ object SettingsManager {
input.copyTo(output)
}
}
Log.i(
ANG_PACKAGE,
"Copied from apk assets folder to ${target.absolutePath}"
)
Log.i(AppConfig.TAG, "Copied from apk assets folder to ${target.absolutePath}")
}
} catch (e: Exception) {
Log.e(ANG_PACKAGE, "asset copy failed", e)
@@ -343,6 +340,7 @@ object SettingsManager {
Language.VIETNAMESE -> Locale("vi")
Language.RUSSIAN -> Locale("ru")
Language.PERSIAN -> Locale("fa")
Language.ARABIC -> Locale("ar")
Language.BANGLA -> Locale("bn")
Language.BAKHTIARI -> Locale("bqi", "IR")
}
@@ -51,7 +51,7 @@ object SpeedtestManager {
return try {
Libv2ray.measureOutboundDelay(config, SettingsManager.getDelayTestUrl())
} catch (e: Exception) {
Log.d(AppConfig.ANG_PACKAGE, "realPing: $e")
Log.e(AppConfig.TAG, "Failed to measure outbound delay", e)
-1L
}
}
@@ -76,7 +76,7 @@ object SpeedtestManager {
}
}
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to ping URL: $url", e)
}
return "-1ms"
}
@@ -103,11 +103,11 @@ object SpeedtestManager {
socket.close()
return time
} catch (e: UnknownHostException) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Unknown host: $url", e)
} catch (e: IOException) {
Log.d(AppConfig.ANG_PACKAGE, "socketConnectTime IOException: $e")
Log.e(AppConfig.TAG, "socketConnectTime IOException: $e")
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to establish socket connection to $url:$port", e)
}
return -1
}
@@ -152,13 +152,13 @@ object SpeedtestManager {
)
}
} catch (e: IOException) {
Log.d(AppConfig.ANG_PACKAGE, "testConnection IOException: " + Log.getStackTraceString(e))
Log.e(AppConfig.TAG, "Connection test IOException", e)
result = context.getString(R.string.connection_test_error, e.message)
} catch (e: Exception) {
Log.d(AppConfig.ANG_PACKAGE, "testConnection Exception: " + Log.getStackTraceString(e))
Log.e(AppConfig.TAG, "Connection test Exception", e)
result = context.getString(R.string.connection_test_error, e.message)
} finally {
conn?.disconnect()
conn.disconnect()
}
return Pair(elapsed, result)
@@ -4,7 +4,6 @@ import android.content.Context
import android.text.TextUtils
import android.util.Log
import com.v2ray.ang.AppConfig
import com.v2ray.ang.AppConfig.ANG_PACKAGE
import com.v2ray.ang.AppConfig.DEFAULT_NETWORK
import com.v2ray.ang.AppConfig.DNS_ALIDNS_ADDRESSES
import com.v2ray.ang.AppConfig.DNS_ALIDNS_DOMAIN
@@ -52,6 +51,7 @@ import com.v2ray.ang.util.JsonUtil
import com.v2ray.ang.util.Utils
object V2rayConfigManager {
private var initConfigCache: String? = null
/**
* Retrieves the V2ray configuration for the given GUID.
@@ -63,47 +63,72 @@ object V2rayConfigManager {
fun getV2rayConfig(context: Context, guid: String): ConfigResult {
try {
val config = MmkvManager.decodeServerConfig(guid) ?: return ConfigResult(false)
if (config.configType == EConfigType.CUSTOM) {
val raw = MmkvManager.decodeServerRaw(guid) ?: return ConfigResult(false)
val domainPort = config.getServerAddressAndPort()
return ConfigResult(true, guid, raw, domainPort)
return if (config.configType == EConfigType.CUSTOM) {
getV2rayCustomConfig(guid, config)
} else {
getV2rayNormalConfig(context, guid, config)
}
val result = getV2rayNonCustomConfig(context, config)
//Log.d(ANG_PACKAGE, result.content)
result.guid = guid
return result
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to get V2ray config", e)
return ConfigResult(false)
}
}
/**
* Retrieves the non-custom V2ray configuration.
* Retrieves the speedtest V2ray configuration for the given GUID.
*
* @param context The context in which the function is called.
* @param context The context of the caller.
* @param guid The unique identifier for the V2ray configuration.
* @return A ConfigResult object containing the configuration details or indicating failure.
*/
fun getV2rayConfig4Speedtest(context: Context, guid: String): ConfigResult {
try {
val config = MmkvManager.decodeServerConfig(guid) ?: return ConfigResult(false)
return if (config.configType == EConfigType.CUSTOM) {
getV2rayCustomConfig(guid, config)
} else {
getV2rayNormalConfig4Speedtest(context, guid, config)
}
} catch (e: Exception) {
Log.e(AppConfig.TAG, "Failed to get V2ray config for speedtest", e)
return ConfigResult(false)
}
}
/**
* Retrieves the custom V2ray configuration.
*
* @param guid The unique identifier for the V2ray configuration.
* @param config The profile item containing the configuration details.
* @return A ConfigResult object containing the result of the configuration retrieval.
*/
private fun getV2rayNonCustomConfig(context: Context, config: ProfileItem): ConfigResult {
private fun getV2rayCustomConfig(guid: String, config: ProfileItem): ConfigResult {
val raw = MmkvManager.decodeServerRaw(guid) ?: return ConfigResult(false)
val domainPort = config.getServerAddressAndPort()
return ConfigResult(true, guid, raw, domainPort)
}
/**
* Retrieves the normal V2ray configuration.
*
* @param context The context in which the function is called.
* @param guid The unique identifier for the V2ray configuration.
* @param config The profile item containing the configuration details.
* @return A ConfigResult object containing the result of the configuration retrieval.
*/
private fun getV2rayNormalConfig(context: Context, guid: String, config: ProfileItem): ConfigResult {
val result = ConfigResult(false)
val address = config.server ?: return result
if (!Utils.isIpAddress(address)) {
if (!Utils.isValidUrl(address)) {
Log.d(ANG_PACKAGE, "$address is an invalid ip or domain")
Log.w(AppConfig.TAG, "$address is an invalid ip or domain")
return result
}
}
val assets = Utils.readTextFromAssets(context, "v2ray_config.json")
if (TextUtils.isEmpty(assets)) {
return result
}
val v2rayConfig = JsonUtil.fromJson(assets, V2rayConfig::class.java) ?: return result
v2rayConfig.log.loglevel =
MmkvManager.decodeSettingsString(AppConfig.PREF_LOGLEVEL) ?: "warning"
val v2rayConfig = initV2rayConfig(context) ?: return result
v2rayConfig.log.loglevel = MmkvManager.decodeSettingsString(AppConfig.PREF_LOGLEVEL) ?: "warning"
v2rayConfig.remarks = config.remarks
inbounds(v2rayConfig)
@@ -129,9 +154,75 @@ object V2rayConfigManager {
result.status = true
result.content = v2rayConfig.toPrettyPrinting()
result.domainPort = if (retMore.first) retMore.second else retOut.second
result.guid = guid
return result
}
/**
* Retrieves the normal V2ray configuration for speedtest.
*
* @param context The context in which the function is called.
* @param guid The unique identifier for the V2ray configuration.
* @param config The profile item containing the configuration details.
* @return A ConfigResult object containing the result of the configuration retrieval.
*/
private fun getV2rayNormalConfig4Speedtest(context: Context, guid: String, config: ProfileItem): ConfigResult {
val result = ConfigResult(false)
val address = config.server ?: return result
if (!Utils.isIpAddress(address)) {
if (!Utils.isValidUrl(address)) {
Log.w(AppConfig.TAG, "$address is an invalid ip or domain")
return result
}
}
val v2rayConfig = initV2rayConfig(context) ?: return result
val isPlugin = config.configType == EConfigType.HYSTERIA2
val retOut = outbounds(v2rayConfig, config, isPlugin) ?: return result
val retMore = moreOutbounds(v2rayConfig, config.subscriptionId, isPlugin)
v2rayConfig.log.loglevel = MmkvManager.decodeSettingsString(AppConfig.PREF_LOGLEVEL) ?: "warning"
v2rayConfig.inbounds.clear()
v2rayConfig.routing.rules.clear()
v2rayConfig.dns = null
v2rayConfig.fakedns = null
v2rayConfig.stats = null
v2rayConfig.policy = null
v2rayConfig.outbounds.forEach { key ->
key.mux = null
}
result.status = true
result.content = v2rayConfig.toPrettyPrinting()
result.domainPort = if (retMore.first) retMore.second else retOut.second
result.guid = guid
return result
}
/**
* Initializes V2ray configuration.
*
* This function loads the V2ray configuration from assets or from a cached value.
* It first attempts to use the cached configuration if available, otherwise reads
* the configuration from the "v2ray_config.json" asset file.
*
* @param context Android context used to access application assets
* @return V2rayConfig object parsed from the JSON configuration, or null if the configuration is empty
*/
private fun initV2rayConfig(context: Context): V2rayConfig? {
val assets = initConfigCache ?: Utils.readTextFromAssets(context, "v2ray_config.json")
if (TextUtils.isEmpty(assets)) {
return null
}
initConfigCache = assets
val config = JsonUtil.fromJson(assets, V2rayConfig::class.java)
return config
}
private fun inbounds(v2rayConfig: V2rayConfig): Boolean {
try {
val socksPort = SettingsManager.getSocksPort()
@@ -164,7 +255,7 @@ object V2rayConfigManager {
}
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to configure inbounds", e)
return false
}
return true
@@ -227,7 +318,7 @@ object V2rayConfigManager {
routingUserRule(key, v2rayConfig)
}
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to configure routing", e)
return false
}
return true
@@ -244,7 +335,7 @@ object V2rayConfigManager {
v2rayConfig.routing.rules.add(rule)
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to apply routing user rule", e)
}
}
@@ -274,7 +365,7 @@ object V2rayConfigManager {
val proxyDomain = userRule2Domain(TAG_PROXY)
val directDomain = userRule2Domain(TAG_DIRECT)
// fakedns with all domains to make it always top priority
v2rayConfig.dns.servers?.add(
v2rayConfig.dns?.servers?.add(
0,
V2rayConfig.DnsBean.ServersBean(
address = "fakedns",
@@ -330,7 +421,7 @@ object V2rayConfigManager {
)
)
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to configure custom local DNS", e)
return false
}
return true
@@ -394,7 +485,7 @@ object V2rayConfigManager {
if (userHostsMap != null) hosts.putAll(userHostsMap)
}
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to configure user DNS hosts", e)
}
//block dns
@@ -433,7 +524,7 @@ object V2rayConfigManager {
)
}
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to configure DNS", e)
return false
}
return true
@@ -504,7 +595,7 @@ object V2rayConfigManager {
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to update outbound with global settings", e)
return false
}
return true
@@ -570,7 +661,7 @@ object V2rayConfigManager {
dialerProxy = TAG_FRAGMENT
)
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to update outbound fragment", e)
return false
}
return true
@@ -633,7 +724,7 @@ object V2rayConfigManager {
}
}
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to configure more outbounds", e)
return returnPair
}
@@ -35,7 +35,7 @@ class TaskerReceiver : BroadcastReceiver() {
V2RayServiceManager.stopVService(context)
}
} catch (e: Exception) {
e.printStackTrace()
android.util.Log.e(AppConfig.TAG, "Error processing Tasker broadcast", e)
}
}
}
@@ -2,7 +2,7 @@ package com.v2ray.ang.service
import android.content.Context
import android.util.Log
import com.v2ray.ang.AppConfig.ANG_PACKAGE
import com.v2ray.ang.AppConfig
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -16,7 +16,7 @@ class ProcessService {
* @param cmd The command to run.
*/
fun runProcess(context: Context, cmd: MutableList<String>) {
Log.d(ANG_PACKAGE, cmd.toString())
Log.i(AppConfig.TAG, cmd.toString())
try {
val proBuilder = ProcessBuilder(cmd)
@@ -27,14 +27,14 @@ class ProcessService {
CoroutineScope(Dispatchers.IO).launch {
Thread.sleep(50L)
Log.d(ANG_PACKAGE, "runProcess check")
Log.i(AppConfig.TAG, "runProcess check")
process?.waitFor()
Log.d(ANG_PACKAGE, "runProcess exited")
Log.i(AppConfig.TAG, "runProcess exited")
}
Log.d(ANG_PACKAGE, process.toString())
Log.i(AppConfig.TAG, process.toString())
} catch (e: Exception) {
Log.d(ANG_PACKAGE, e.toString())
Log.e(AppConfig.TAG, e.toString(), e)
}
}
@@ -43,10 +43,10 @@ class ProcessService {
*/
fun stopProcess() {
try {
Log.d(ANG_PACKAGE, "runProcess destroy")
Log.i(AppConfig.TAG, "runProcess destroy")
process?.destroy()
} catch (e: Exception) {
Log.d(ANG_PACKAGE, e.toString())
Log.e(AppConfig.TAG, "Failed to destroy process", e)
}
}
}
@@ -8,6 +8,7 @@ import android.graphics.drawable.Icon
import android.os.Build
import android.service.quicksettings.Tile
import android.service.quicksettings.TileService
import android.util.Log
import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat
import com.v2ray.ang.AppConfig
@@ -61,7 +62,7 @@ class QSTileService : TileService() {
applicationContext.unregisterReceiver(mMsgReceive)
mMsgReceive = null
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to unregister receiver", e)
}
}
@@ -38,7 +38,7 @@ object SubscriptionUpdater {
*/
@SuppressLint("MissingPermission")
override suspend fun doWork(): Result {
Log.d(AppConfig.ANG_PACKAGE, "subscription automatic update starting")
Log.i(AppConfig.TAG, "subscription automatic update starting")
val subs = MmkvManager.decodeSubscriptions().filter { it.second.autoUpdate }
@@ -56,10 +56,7 @@ object SubscriptionUpdater {
notificationManager.createNotificationChannel(channel)
}
notificationManager.notify(3, notification.build())
Log.d(
AppConfig.ANG_PACKAGE,
"subscription automatic update: ---${subItem.remarks}"
)
Log.i(AppConfig.TAG, "subscription automatic update: ---${subItem.remarks}")
updateConfigViaSub(Pair(sub.first, subItem))
notification.setContentText("Updating ${subItem.remarks}")
}
@@ -9,7 +9,6 @@ import android.os.Build
import android.util.Log
import androidx.core.content.ContextCompat
import com.v2ray.ang.AppConfig
import com.v2ray.ang.AppConfig.ANG_PACKAGE
import com.v2ray.ang.R
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.dto.ProfileItem
@@ -144,7 +143,7 @@ object V2RayServiceManager {
mFilter.addAction(Intent.ACTION_USER_PRESENT)
ContextCompat.registerReceiver(service, mMsgReceive, mFilter, Utils.receiverFlags())
} catch (e: Exception) {
Log.d(ANG_PACKAGE, e.toString())
Log.e(AppConfig.TAG, "Failed to register broadcast receiver", e)
}
v2rayPoint.configureFileContent = result.content
@@ -154,7 +153,7 @@ object V2RayServiceManager {
try {
v2rayPoint.runLoop(MmkvManager.decodeSettingsBool(AppConfig.PREF_PREFER_IPV6))
} catch (e: Exception) {
Log.d(ANG_PACKAGE, e.toString())
Log.e(AppConfig.TAG, "Failed to start V2Ray loop", e)
}
if (v2rayPoint.isRunning) {
@@ -179,7 +178,7 @@ object V2RayServiceManager {
try {
v2rayPoint.stopLoop()
} catch (e: Exception) {
Log.d(ANG_PACKAGE, e.toString())
Log.e(AppConfig.TAG, "Failed to stop V2Ray loop", e)
}
}
}
@@ -190,7 +189,7 @@ object V2RayServiceManager {
try {
service.unregisterReceiver(mMsgReceive)
} catch (e: Exception) {
Log.d(ANG_PACKAGE, e.toString())
Log.e(AppConfig.TAG, "Failed to unregister broadcast receiver", e)
}
PluginUtil.stopPlugin()
}
@@ -217,14 +216,14 @@ object V2RayServiceManager {
try {
time = v2rayPoint.measureDelay(SettingsManager.getDelayTestUrl())
} catch (e: Exception) {
Log.d(ANG_PACKAGE, "measureV2rayDelay: $e")
Log.e(AppConfig.TAG, "Failed to measure delay with primary URL", e)
errstr = e.message?.substringAfter("\":") ?: "empty message"
}
if (time == -1L) {
try {
time = v2rayPoint.measureDelay(SettingsManager.getDelayTestUrl(true))
} catch (e: Exception) {
Log.d(ANG_PACKAGE, "measureV2rayDelay: $e")
Log.e(AppConfig.TAG, "Failed to measure delay with alternative URL", e)
errstr = e.message?.substringAfter("\":") ?: "empty message"
}
}
@@ -255,7 +254,7 @@ object V2RayServiceManager {
serviceControl.stopService()
0
} catch (e: Exception) {
Log.d(ANG_PACKAGE, e.toString())
Log.e(AppConfig.TAG, "Failed to stop service in callback", e)
-1
}
}
@@ -291,7 +290,7 @@ object V2RayServiceManager {
NotificationService.startSpeedNotification(currentConfig)
0
} catch (e: Exception) {
Log.d(ANG_PACKAGE, e.toString())
Log.e(AppConfig.TAG, "Failed to setup service in callback", e)
-1
}
}
@@ -323,12 +322,12 @@ object V2RayServiceManager {
}
AppConfig.MSG_STATE_STOP -> {
Log.d(ANG_PACKAGE, "Stop Service")
Log.i(AppConfig.TAG, "Stop Service")
serviceControl.stopService()
}
AppConfig.MSG_STATE_RESTART -> {
Log.d(ANG_PACKAGE, "Restart Service")
Log.i(AppConfig.TAG, "Restart Service")
serviceControl.stopService()
Thread.sleep(500L)
startVService(serviceControl.getService())
@@ -341,12 +340,12 @@ object V2RayServiceManager {
when (intent?.action) {
Intent.ACTION_SCREEN_OFF -> {
Log.d(ANG_PACKAGE, "SCREEN_OFF, stop querying stats")
Log.i(AppConfig.TAG, "SCREEN_OFF, stop querying stats")
NotificationService.stopSpeedNotification(currentConfig)
}
Intent.ACTION_SCREEN_ON -> {
Log.d(ANG_PACKAGE, "SCREEN_ON, start querying stats")
Log.i(AppConfig.TAG, "SCREEN_ON, start querying stats")
NotificationService.startSpeedNotification(currentConfig)
}
}
@@ -81,11 +81,11 @@ class V2RayTestService : Service() {
val delay = PluginUtil.realPingHy2(this, config)
return delay
} else {
val config = V2rayConfigManager.getV2rayConfig(this, guid)
if (!config.status) {
val configResult = V2rayConfigManager.getV2rayConfig4Speedtest(this, guid)
if (!configResult.status) {
return retFailure
}
return SpeedtestManager.realPing(config.content)
return SpeedtestManager.realPing(configResult.content)
}
}
}
@@ -18,7 +18,6 @@ import android.os.StrictMode
import android.util.Log
import androidx.annotation.RequiresApi
import com.v2ray.ang.AppConfig
import com.v2ray.ang.AppConfig.ANG_PACKAGE
import com.v2ray.ang.AppConfig.LOOPBACK
import com.v2ray.ang.BuildConfig
import com.v2ray.ang.R
@@ -40,6 +39,7 @@ class V2RayVpnService : VpnService(), ServiceControl {
private const val PRIVATE_VLAN6_CLIENT = "fc00::10:10:14:1"
private const val PRIVATE_VLAN6_ROUTER = "fc00::10:10:14:2"
private const val TUN2SOCKS = "libtun2socks.so"
}
private lateinit var mInterface: ParcelFileDescriptor
@@ -209,7 +209,7 @@ class V2RayVpnService : VpnService(), ServiceControl {
else
builder.addAllowedApplication(it)
} catch (e: PackageManager.NameNotFoundException) {
Log.d(ANG_PACKAGE, "setup error : --${e.localizedMessage}")
Log.e(AppConfig.TAG, "Failed to configure app in VPN: ${e.localizedMessage}", e)
}
}
} else {
@@ -227,7 +227,7 @@ class V2RayVpnService : VpnService(), ServiceControl {
try {
connectivity.requestNetwork(defaultNetworkRequest, defaultNetworkCallback)
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to request default network", e)
}
}
@@ -245,7 +245,7 @@ class V2RayVpnService : VpnService(), ServiceControl {
return true
} catch (e: Exception) {
// non-nullable lateinit var
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to establish VPN interface", e)
stopV2Ray()
}
return false
@@ -277,7 +277,7 @@ class V2RayVpnService : VpnService(), ServiceControl {
cmd.add("--dnsgw")
cmd.add("$LOOPBACK:${localDnsPort}")
}
Log.d(packageName, cmd.toString())
Log.i(AppConfig.TAG, cmd.toString())
try {
val proBuilder = ProcessBuilder(cmd)
@@ -286,19 +286,19 @@ class V2RayVpnService : VpnService(), ServiceControl {
.directory(applicationContext.filesDir)
.start()
Thread {
Log.d(packageName, "$TUN2SOCKS check")
Log.i(AppConfig.TAG, "$TUN2SOCKS check")
process.waitFor()
Log.d(packageName, "$TUN2SOCKS exited")
Log.i(AppConfig.TAG, "$TUN2SOCKS exited")
if (isRunning) {
Log.d(packageName, "$TUN2SOCKS restart")
Log.i(AppConfig.TAG, "$TUN2SOCKS restart")
runTun2socks()
}
}.start()
Log.d(packageName, process.toString())
Log.i(AppConfig.TAG, process.toString())
sendFd()
} catch (e: Exception) {
Log.d(packageName, e.toString())
Log.e(AppConfig.TAG, "Failed to start tun2socks process", e)
}
}
@@ -309,13 +309,13 @@ class V2RayVpnService : VpnService(), ServiceControl {
private fun sendFd() {
val fd = mInterface.fileDescriptor
val path = File(applicationContext.filesDir, "sock_path").absolutePath
Log.d(packageName, path)
Log.i(AppConfig.TAG, path)
CoroutineScope(Dispatchers.IO).launch {
var tries = 0
while (true) try {
Thread.sleep(50L shl tries)
Log.d(packageName, "sendFd tries: $tries")
Log.i(AppConfig.TAG, "sendFd tries: $tries")
LocalSocket().use { localSocket ->
localSocket.connect(LocalSocketAddress(path, LocalSocketAddress.Namespace.FILESYSTEM))
localSocket.setFileDescriptorsForSend(arrayOf(fd))
@@ -323,7 +323,7 @@ class V2RayVpnService : VpnService(), ServiceControl {
}
break
} catch (e: Exception) {
Log.d(packageName, e.toString())
Log.e(AppConfig.TAG, "Failed to send file descriptor, try: $tries", e)
if (tries > 5) break
tries += 1
}
@@ -349,10 +349,10 @@ class V2RayVpnService : VpnService(), ServiceControl {
}
try {
Log.d(packageName, "tun2socks destroy")
Log.i(AppConfig.TAG, "tun2socks destroy")
process.destroy()
} catch (e: Exception) {
Log.d(packageName, e.toString())
Log.e(AppConfig.TAG, "Failed to destroy tun2socks process", e)
}
V2RayServiceManager.stopV2rayPoint()
@@ -367,8 +367,8 @@ class V2RayVpnService : VpnService(), ServiceControl {
try {
mInterface.close()
} catch (ignored: Exception) {
// ignored
} catch (e: Exception) {
Log.e(AppConfig.TAG, "Failed to close VPN interface", e)
}
}
}
@@ -34,7 +34,7 @@ class AboutActivity : BaseActivity() {
try {
showFileChooser()
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to show file chooser", e)
}
} else {
toast(R.string.toast_permission_denied)
@@ -90,7 +90,7 @@ class AboutActivity : BaseActivity() {
try {
showFileChooser()
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to show file chooser", e)
}
} else {
requestPermissionLauncher.launch(permission)
@@ -106,7 +106,7 @@ class AboutActivity : BaseActivity() {
}
binding.layoutOssLicenses.setOnClickListener {
val webView = android.webkit.WebView(this);
val webView = android.webkit.WebView(this)
webView.loadUrl("file:///android_asset/open_source_licenses.html")
android.app.AlertDialog.Builder(this)
.setTitle("Open source licenses")
@@ -169,7 +169,7 @@ class AboutActivity : BaseActivity() {
try {
chooseFile.launch(Intent.createChooser(intent, getString(R.string.title_file_chooser)))
} catch (ex: android.content.ActivityNotFoundException) {
Log.e(AppConfig.ANG_PACKAGE, "File chooser activity not found: ${ex.message}", ex)
Log.e(AppConfig.TAG, "File chooser activity not found", ex)
toast(R.string.toast_require_file_manager)
}
}
@@ -192,7 +192,7 @@ class AboutActivity : BaseActivity() {
toastError(R.string.toast_failure)
}
} catch (e: Exception) {
Log.e(AppConfig.ANG_PACKAGE, "Error during file restore: ${e.message}", e)
Log.e(AppConfig.TAG, "Error during file restore", e)
toastError(R.string.toast_failure)
}
}
@@ -2,12 +2,14 @@ package com.v2ray.ang.ui
import android.annotation.SuppressLint
import android.os.Bundle
import android.util.Log
import android.view.Menu
import android.view.MenuItem
import androidx.appcompat.widget.SearchView
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.v2ray.ang.AppConfig
import com.v2ray.ang.AppConfig.ANG_PACKAGE
import com.v2ray.ang.R
import com.v2ray.ang.databinding.ActivityLogcatBinding
@@ -68,7 +70,7 @@ class LogcatActivity : BaseActivity(), SwipeRefreshLayout.OnRefreshListener {
}
}
} catch (e: IOException) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to get logcat", e)
}
}
@@ -89,7 +91,7 @@ class LogcatActivity : BaseActivity(), SwipeRefreshLayout.OnRefreshListener {
}
}
} catch (e: IOException) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to clear logcat", e)
}
}
@@ -1,13 +1,16 @@
package com.v2ray.ang.ui
import android.util.Log
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.v2ray.ang.AppConfig
import com.v2ray.ang.databinding.ItemRecyclerLogcatBinding
class LogcatRecyclerAdapter(val activity: LogcatActivity) : RecyclerView.Adapter<LogcatRecyclerAdapter.MainViewHolder>() {
private var mActivity: LogcatActivity = activity
override fun getItemCount() = mActivity.logsets.size
override fun onBindViewHolder(holder: MainViewHolder, position: Int) {
@@ -22,7 +25,7 @@ class LogcatRecyclerAdapter(val activity: LogcatActivity) : RecyclerView.Adapter
holder.itemSubSettingBinding.logContent.text = if (content.count() > 1) content.last().trim() else ""
}
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Error binding log view data", e)
}
}
@@ -9,6 +9,7 @@ import android.net.Uri
import android.net.VpnService
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.KeyEvent
import android.view.Menu
import android.view.MenuItem
@@ -485,7 +486,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
val clipboard = Utils.getClipboard(this)
importBatchConfig(clipboard)
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to import config from clipboard", e)
return false
}
return true
@@ -515,16 +516,19 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
toastError(R.string.toast_failure)
binding.pbWaiting.hide()
}
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to import batch config", e)
}
}
}
/**
* import config from local config file
*/
private fun importConfigLocal(): Boolean {
try {
showFileChooser()
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to import config from local file", e)
return false
}
return true
@@ -744,7 +748,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
importBatchConfig(input?.bufferedReader()?.readText())
}
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to read content from URI", e)
}
} else {
requestPermissionLauncher.launch(permission)
@@ -807,6 +811,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
Intent(this, SettingsActivity::class.java)
.putExtra("isRunning", mainViewModel.isRunning.value == true)
)
R.id.promotion -> Utils.openUri(this, "${Utils.decode(AppConfig.PromotionUrl)}?t=${System.currentTimeMillis()}")
R.id.logcat -> startActivity(Intent(this, LogcatActivity::class.java))
R.id.about -> startActivity(Intent(this, AboutActivity::class.java))
@@ -3,6 +3,7 @@ package com.v2ray.ang.ui
import android.content.Intent
import android.graphics.Color
import android.text.TextUtils
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -187,7 +188,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
else -> mActivity.toast("else")
}
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Error when sharing server", e)
}
}.show()
}
@@ -299,7 +300,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
delay(500)
V2RayServiceManager.startVService(mActivity)
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to restart V2Ray service", e)
}
}
}
@@ -31,6 +31,7 @@ class PerAppProxyActivity : BaseActivity() {
ActivityBypassListBinding.inflate(layoutInflater)
}
private var adapter: PerAppProxyAdapter? = null
private var appsAll: List<AppInfo>? = null
@@ -164,7 +165,7 @@ class PerAppProxyActivity : BaseActivity() {
content = HttpUtil.getUrlContent(url, 5000, httpPort) ?: ""
}
launch(Dispatchers.Main) {
Log.d(ANG_PACKAGE, content)
Log.i(AppConfig.TAG, content)
selectProxyApp(content, true)
toastSuccess(R.string.toast_success)
binding.pbWaiting.hide()
@@ -205,7 +206,7 @@ class PerAppProxyActivity : BaseActivity() {
adapter?.let { it ->
it.apps.forEach block@{
val packageName = it.packageName
Log.d(ANG_PACKAGE, packageName)
Log.i(AppConfig.TAG, packageName)
if (!inProxyApps(proxyApps, packageName, force)) {
adapter?.blacklist?.add(packageName)
println(packageName)
@@ -218,7 +219,7 @@ class PerAppProxyActivity : BaseActivity() {
adapter?.let { it ->
it.apps.forEach block@{
val packageName = it.packageName
Log.d(ANG_PACKAGE, packageName)
Log.i(AppConfig.TAG, packageName)
if (inProxyApps(proxyApps, packageName, force)) {
adapter?.blacklist?.add(packageName)
println(packageName)
@@ -229,7 +230,7 @@ class PerAppProxyActivity : BaseActivity() {
}
}
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Error selecting proxy app", e)
return false
}
return true
@@ -4,6 +4,7 @@ import android.Manifest
import android.annotation.SuppressLint
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.view.Menu
import android.view.MenuItem
import android.view.View
@@ -110,7 +111,7 @@ class RoutingSettingActivity : BaseActivity() {
}
}
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to import predefined ruleset", e)
}
}
.setNegativeButton(android.R.string.cancel) { _, _ ->
@@ -126,7 +127,7 @@ class RoutingSettingActivity : BaseActivity() {
val clipboard = try {
Utils.getClipboard(this)
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to get clipboard content", e)
toastError(R.string.toast_failure)
return@setPositiveButton
}
@@ -6,6 +6,7 @@ import android.content.pm.PackageManager
import android.graphics.BitmapFactory
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.Menu
import android.view.MenuItem
import androidx.activity.result.contract.ActivityResultContracts
@@ -21,6 +22,7 @@ import io.github.g00fy2.quickie.config.ScannerConfig
class ScannerActivity : BaseActivity() {
private val scanQrCode = registerForActivityResult(ScanCustomCode(), ::handleResult)
private val chooseFile = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
val uri = it.data?.data
@@ -37,7 +39,7 @@ class ScannerActivity : BaseActivity() {
finished(text)
}
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to decode QR code from file", e)
toast(R.string.toast_decoding_failed)
}
}
@@ -14,7 +14,6 @@ import android.widget.Spinner
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import com.v2ray.ang.AppConfig
import com.v2ray.ang.AppConfig.ANG_PACKAGE
import com.v2ray.ang.AppConfig.DEFAULT_PORT
import com.v2ray.ang.AppConfig.PREF_ALLOW_INSECURE
import com.v2ray.ang.AppConfig.REALITY
@@ -481,7 +480,7 @@ class ServerActivity : BaseActivity() {
if (config.subscriptionId.isEmpty() && !subscriptionId.isNullOrEmpty()) {
config.subscriptionId = subscriptionId.orEmpty()
}
Log.d(ANG_PACKAGE, JsonUtil.toJsonPretty(config) ?: "")
//Log.i(AppConfig.TAG, JsonUtil.toJsonPretty(config) ?: "")
MmkvManager.encodeServerConfig(editGuid, config)
toastSuccess(R.string.toast_success)
finish()
@@ -2,11 +2,13 @@ package com.v2ray.ang.ui
import android.os.Bundle
import android.text.TextUtils
import android.util.Log
import android.view.Menu
import android.view.MenuItem
import androidx.appcompat.app.AlertDialog
import com.blacksquircle.ui.editorkit.utils.EditorTheme
import com.blacksquircle.ui.language.json.JsonLanguage
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
import com.v2ray.ang.databinding.ActivityServerCustomConfigBinding
import com.v2ray.ang.dto.EConfigType
@@ -76,7 +78,7 @@ class ServerCustomConfigActivity : BaseActivity() {
val profileItem = try {
CustomFmt.parse(binding.editor.text.toString())
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to parse custom configuration", e)
toast("${getString(R.string.toast_malformed_josn)} ${e.cause?.message}")
return false
}
@@ -3,11 +3,13 @@ package com.v2ray.ang.ui
import android.content.Intent
import android.graphics.Color
import android.text.TextUtils
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import androidx.recyclerview.widget.RecyclerView
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
import com.v2ray.ang.databinding.ItemQrcodeBinding
import com.v2ray.ang.databinding.ItemRecyclerSubSettingBinding
@@ -81,7 +83,7 @@ class SubSettingRecyclerAdapter(val activity: SubSettingActivity) : RecyclerView
else -> mActivity.toast("else")
}
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Share subscription failed", e)
}
}.show()
}
@@ -3,6 +3,7 @@ package com.v2ray.ang.ui
import android.content.Intent
import android.os.Bundle
import android.text.TextUtils
import android.util.Log
import android.view.Menu
import android.view.MenuItem
import android.view.View
@@ -60,7 +61,7 @@ class TaskerActivity : BaseActivity() {
}
}
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to initialize Tasker settings", e)
}
}
@@ -5,6 +5,7 @@ import android.net.Uri
import android.os.Bundle
import android.util.Log
import androidx.lifecycle.lifecycleScope
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
import com.v2ray.ang.databinding.ActivityLogcatBinding
import com.v2ray.ang.extension.toast
@@ -54,7 +55,7 @@ class UrlSchemeActivity : BaseActivity() {
startActivity(Intent(this, MainActivity::class.java))
finish()
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Error processing URL scheme", e)
}
}
@@ -62,7 +63,7 @@ class UrlSchemeActivity : BaseActivity() {
if (uriString.isNullOrEmpty()) {
return
}
Log.d("UrlScheme", uriString)
Log.i(AppConfig.TAG, uriString)
var decodedUrl = URLDecoder.decode(uriString, "UTF-8")
val uri = Uri.parse(decodedUrl)
@@ -70,7 +71,7 @@ class UrlSchemeActivity : BaseActivity() {
if (uri.fragment.isNullOrEmpty() && !fragment.isNullOrEmpty()) {
decodedUrl += "#${fragment}"
}
Log.d("UrlScheme-decodedUrl", decodedUrl)
Log.i(AppConfig.TAG, decodedUrl)
lifecycleScope.launch(Dispatchers.IO) {
val (count, countSub) = AngConfigManager.importBatchConfig(decodedUrl, "", false)
withContext(Dispatchers.Main) {
@@ -44,6 +44,7 @@ import java.util.Date
class UserAssetActivity : BaseActivity() {
private val binding by lazy { ActivitySubSettingBinding.inflate(layoutInflater) }
val extDir by lazy { File(Utils.userAssetPath(this)) }
val builtInGeoFiles = arrayOf("geosite.dat", "geoip.dat")
@@ -163,7 +164,7 @@ class UserAssetActivity : BaseActivity() {
}.also { cursor.close() }
}
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to get cursor name", e)
null
}
@@ -190,7 +191,7 @@ class UserAssetActivity : BaseActivity() {
.putExtra(UserAssetUrlActivity.ASSET_URL_QRCODE, url)
)
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to import asset from URL", e)
return false
}
return true
@@ -207,12 +208,16 @@ class UserAssetActivity : BaseActivity() {
var resultCount = 0
lifecycleScope.launch(Dispatchers.IO) {
assets.forEach {
var result = downloadGeo(it.second, 15000, httpPort)
if (!result) {
result = downloadGeo(it.second, 15000, 0)
try {
var result = downloadGeo(it.second, 15000, httpPort)
if (!result) {
result = downloadGeo(it.second, 15000, 0)
}
if (result)
resultCount++
} catch (e: Exception) {
Log.e(AppConfig.TAG, "Failed to download geo file: ${it.second.remarks}", e)
}
if (result)
resultCount++
}
withContext(Dispatchers.Main) {
if (resultCount > 0) {
@@ -229,7 +234,7 @@ class UserAssetActivity : BaseActivity() {
private fun downloadGeo(item: AssetUrlItem, timeout: Int, httpPort: Int): Boolean {
val targetTemp = File(extDir, item.remarks + "_temp")
val target = File(extDir, item.remarks)
//Log.d(AppConfig.ANG_PACKAGE, url)
Log.i(AppConfig.TAG, "Downloading geo file: ${item.remarks} from ${item.url}")
val conn = HttpUtil.createProxyConnection(item.url, httpPort, timeout, timeout, needStream = true) ?: return false
try {
@@ -244,10 +249,10 @@ class UserAssetActivity : BaseActivity() {
}
return true
} catch (e: Exception) {
Log.e(AppConfig.ANG_PACKAGE, Log.getStackTraceString(e))
Log.e(AppConfig.TAG, "Failed to download geo file: ${item.remarks}", e)
return false
} finally {
conn?.disconnect()
conn.disconnect()
}
}
@@ -2,9 +2,11 @@ package com.v2ray.ang.ui
import android.os.Bundle
import android.text.TextUtils
import android.util.Log
import android.view.Menu
import android.view.MenuItem
import androidx.appcompat.app.AlertDialog
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
import com.v2ray.ang.databinding.ActivityUserAssetUrlBinding
import com.v2ray.ang.dto.AssetUrlItem
@@ -75,7 +77,11 @@ class UserAssetUrlActivity : BaseActivity() {
// remove file associated with the asset
val file = extDir.resolve(assetItem.remarks)
if (file.exists()) {
file.delete()
try {
file.delete()
} catch (e: Exception) {
Log.e(AppConfig.TAG, "Failed to delete asset file: ${file.path}", e)
}
}
} else {
assetId = Utils.getUuid()
@@ -1,5 +1,7 @@
package com.v2ray.ang.util
import android.util.Log
import com.v2ray.ang.AppConfig
import com.v2ray.ang.AppConfig.LOOPBACK
import com.v2ray.ang.BuildConfig
import com.v2ray.ang.util.Utils.encode
@@ -10,6 +12,7 @@ import java.util.*
object HttpUtil {
/**
* Converts a URL string to its ASCII representation.
*
@@ -142,7 +145,7 @@ object HttpUtil {
)
}
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to create proxy connection", e)
// If an exception occurs, close the connection and return null
conn?.disconnect()
return null
@@ -1,5 +1,6 @@
package com.v2ray.ang.util
import android.util.Log
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.JsonObject
@@ -8,6 +9,7 @@ import com.google.gson.JsonPrimitive
import com.google.gson.JsonSerializationContext
import com.google.gson.JsonSerializer
import com.google.gson.reflect.TypeToken
import com.v2ray.ang.AppConfig
import java.lang.reflect.Type
object JsonUtil {
@@ -70,7 +72,7 @@ object JsonUtil {
try {
return JsonParser.parseString(src).getAsJsonObject()
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to parse JSON string", e)
return null
}
}
@@ -3,12 +3,14 @@ package com.v2ray.ang.util
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.util.Log
import com.v2ray.ang.AppConfig
import com.v2ray.ang.service.V2RayTestService
import java.io.Serializable
object MessageUtil {
/**
* Sends a message to the service.
*
@@ -46,7 +48,7 @@ object MessageUtil {
intent.putExtra("content", content)
ctx.startService(intent)
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to send message to test service", e)
}
}
@@ -67,7 +69,7 @@ object MessageUtil {
intent.putExtra("content", content)
ctx.sendBroadcast(intent)
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to send message with action: $action", e)
}
}
}
@@ -3,7 +3,7 @@ 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.AppConfig
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.dto.ProfileItem
import com.v2ray.ang.fmt.Hysteria2Fmt
@@ -13,7 +13,7 @@ import java.io.File
object PluginUtil {
private const val HYSTERIA2 = "libhysteria2.so"
private const val TAG = ANG_PACKAGE
private val procService: ProcessService by lazy {
ProcessService()
}
@@ -26,13 +26,23 @@ object PluginUtil {
* @param domainPort The domain and port information.
*/
fun runPlugin(context: Context, config: ProfileItem?, domainPort: String?) {
Log.d(TAG, "runPlugin")
Log.i(AppConfig.TAG, "Starting plugin execution")
if (config?.configType?.equals(EConfigType.HYSTERIA2) == true) {
val configFile = genConfigHy2(context, config, domainPort) ?: return
val cmd = genCmdHy2(context, configFile)
if (config == null) {
Log.w(AppConfig.TAG, "Cannot run plugin: config is null")
return
}
try {
if (config.configType == EConfigType.HYSTERIA2) {
Log.i(AppConfig.TAG, "Running Hysteria2 plugin")
val configFile = genConfigHy2(context, config, domainPort) ?: return
val cmd = genCmdHy2(context, configFile)
procService.runProcess(context, cmd)
procService.runProcess(context, cmd)
}
} catch (e: Exception) {
Log.e(AppConfig.TAG, "Error running plugin", e)
}
}
@@ -51,7 +61,7 @@ object PluginUtil {
* @return The ping delay in milliseconds, or -1 if it fails.
*/
fun realPingHy2(context: Context, config: ProfileItem?): Long {
Log.d(TAG, "realPingHy2")
Log.i(AppConfig.TAG, "realPingHy2")
val retFailure = -1L
if (config?.configType?.equals(EConfigType.HYSTERIA2) == true) {
@@ -79,18 +89,18 @@ object PluginUtil {
* @return The generated configuration file.
*/
private fun genConfigHy2(context: Context, config: ProfileItem, domainPort: String?): File? {
Log.d(TAG, "runPlugin $HYSTERIA2")
Log.i(AppConfig.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}")
Log.i(AppConfig.TAG, "runPlugin ${configFile.absolutePath}")
configFile.parentFile?.mkdirs()
configFile.writeText(JsonUtil.toJson(hy2Config))
Log.d(TAG, JsonUtil.toJson(hy2Config))
Log.i(AppConfig.TAG, JsonUtil.toJson(hy2Config))
return configFile
}
@@ -119,10 +129,10 @@ object PluginUtil {
*/
private fun stopHy2() {
try {
Log.d(TAG, "$HYSTERIA2 destroy")
Log.i(AppConfig.TAG, "$HYSTERIA2 destroy")
procService?.stopProcess()
} catch (e: Exception) {
Log.d(TAG, e.toString())
Log.e(AppConfig.TAG, "Failed to stop Hysteria2 process", e)
}
}
}
@@ -28,6 +28,10 @@ import java.util.UUID
object Utils {
private val IPV4_REGEX =
Regex("^([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])$")
private val IPV6_REGEX = Regex("^((?:[0-9A-Fa-f]{1,4}))?((?::[0-9A-Fa-f]{1,4}))*::((?:[0-9A-Fa-f]{1,4}))?((?::[0-9A-Fa-f]{1,4}))*|((?:[0-9A-Fa-f]{1,4}))((?::[0-9A-Fa-f]{1,4})){7}$")
/**
* Convert string to editable for Kotlin.
*
@@ -46,22 +50,7 @@ object Utils {
* @return The index of the value in the array, or -1 if not found.
*/
fun arrayFind(array: Array<out String>, value: String): Int {
for (i in array.indices) {
if (array[i] == value) {
return i
}
}
return -1
}
/**
* Parse a string to an integer.
*
* @param str The string to parse.
* @return The parsed integer, or 0 if parsing fails.
*/
fun parseInt(str: String): Int {
return parseInt(str, 0)
return array.indexOf(value)
}
/**
@@ -71,7 +60,7 @@ object Utils {
* @param default The default value if parsing fails.
* @return The parsed integer, or the default value if parsing fails.
*/
fun parseInt(str: String?, default: Int): Int {
fun parseInt(str: String?, default: Int = 0): Int {
return str?.toIntOrNull() ?: default
}
@@ -86,7 +75,7 @@ object Utils {
val cmb = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
cmb.primaryClip?.getItemAt(0)?.text.toString()
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to get clipboard content", e)
""
}
}
@@ -103,7 +92,7 @@ object Utils {
val clipData = ClipData.newPlainText(null, content)
cmb.setPrimaryClip(clipData)
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to set clipboard content", e)
}
}
@@ -124,15 +113,17 @@ object Utils {
* @return The decoded string, or null if decoding fails.
*/
fun tryDecodeBase64(text: String?): String? {
if (text.isNullOrEmpty()) return null
try {
return Base64.decode(text, Base64.NO_WRAP).toString(Charsets.UTF_8)
} catch (e: Exception) {
Log.i(ANG_PACKAGE, "Parse base64 standard failed $e")
Log.e(AppConfig.TAG, "Failed to decode standard base64", e)
}
try {
return Base64.decode(text, Base64.NO_WRAP.or(Base64.URL_SAFE)).toString(Charsets.UTF_8)
} catch (e: Exception) {
Log.i(ANG_PACKAGE, "Parse base64 url safe failed $e")
Log.e(AppConfig.TAG, "Failed to decode URL-safe base64", e)
}
return null
}
@@ -147,7 +138,7 @@ object Utils {
return try {
Base64.encodeToString(text.toByteArray(Charsets.UTF_8), Base64.NO_WRAP)
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to encode text to base64", e)
""
}
}
@@ -159,43 +150,38 @@ object Utils {
* @return True if the string is a valid IP address, false otherwise.
*/
fun isIpAddress(value: String?): Boolean {
if (value.isNullOrEmpty()) return false
try {
if (value.isNullOrEmpty()) {
return false
}
var addr = value
if (addr.isEmpty() || addr.isBlank()) {
return false
}
var addr = value.trim()
if (addr.isEmpty()) return false
//CIDR
if (addr.indexOf("/") > 0) {
if (addr.contains("/")) {
val arr = addr.split("/")
if (arr.count() == 2 && Integer.parseInt(arr[1]) > -1) {
if (arr.size == 2 && arr[1].toIntOrNull() != null && arr[1].toInt() > -1) {
addr = arr[0]
}
}
// "::ffff:192.168.173.22"
// "[::ffff:192.168.173.22]:80"
// Handle IPv4-mapped IPv6 addresses
if (addr.startsWith("::ffff:") && '.' in addr) {
addr = addr.drop(7)
} else if (addr.startsWith("[::ffff:") && '.' in addr) {
addr = addr.drop(8).replace("]", "")
}
// addr = addr.toLowerCase()
val octets = addr.split('.').toTypedArray()
val octets = addr.split('.')
if (octets.size == 4) {
if (octets[3].indexOf(":") > 0) {
if (octets[3].contains(":")) {
addr = addr.substring(0, addr.indexOf(":"))
}
return isIpv4Address(addr)
}
// Ipv6addr [2001:abc::123]:8080
return isIpv6Address(addr)
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to validate IP address", e)
return false
}
}
@@ -217,9 +203,7 @@ object Utils {
* @return True if the string is a valid IPv4 address, false otherwise.
*/
private fun isIpv4Address(value: String): Boolean {
val regV4 =
Regex("^([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])$")
return regV4.matches(value)
return IPV4_REGEX.matches(value)
}
/**
@@ -230,13 +214,10 @@ object Utils {
*/
private fun isIpv6Address(value: String): Boolean {
var addr = value
if (addr.indexOf("[") == 0 && addr.lastIndexOf("]") > 0) {
addr = addr.drop(1)
addr = addr.dropLast(addr.count() - addr.lastIndexOf("]"))
if (addr.startsWith("[") && addr.endsWith("]")) {
addr = addr.drop(1).dropLast(1)
}
val regV6 =
Regex("^((?:[0-9A-Fa-f]{1,4}))?((?::[0-9A-Fa-f]{1,4}))*::((?:[0-9A-Fa-f]{1,4}))?((?::[0-9A-Fa-f]{1,4}))*|((?:[0-9A-Fa-f]{1,4}))((?::[0-9A-Fa-f]{1,4})){7}$")
return regV6.matches(addr)
return IPV6_REGEX.matches(addr)
}
/**
@@ -246,10 +227,10 @@ object Utils {
* @return True if the string is a CoreDNS address, false otherwise.
*/
fun isCoreDNSAddress(s: String): Boolean {
return s.startsWith("https")
|| s.startsWith("tcp")
|| s.startsWith("quic")
|| s == "localhost"
return s.startsWith("https") ||
s.startsWith("tcp") ||
s.startsWith("quic") ||
s == "localhost"
}
/**
@@ -259,21 +240,16 @@ object Utils {
* @return True if the string is a valid URL, false otherwise.
*/
fun isValidUrl(value: String?): Boolean {
try {
if (value.isNullOrEmpty()) {
return false
}
if (Patterns.WEB_URL.matcher(value).matches()
|| Patterns.DOMAIN_NAME.matcher(value).matches()
|| URLUtil.isValidUrl(value)
) {
return true
}
if (value.isNullOrEmpty()) return false
return try {
Patterns.WEB_URL.matcher(value).matches() ||
Patterns.DOMAIN_NAME.matcher(value).matches() ||
URLUtil.isValidUrl(value)
} catch (e: Exception) {
e.printStackTrace()
return false
Log.e(AppConfig.TAG, "Failed to validate URL", e)
false
}
return false
}
/**
@@ -283,8 +259,12 @@ object Utils {
* @param uriString The URI string to open.
*/
fun openUri(context: Context, uriString: String) {
val uri = uriString.toUri()
context.startActivity(Intent(Intent.ACTION_VIEW, uri))
try {
val uri = uriString.toUri()
context.startActivity(Intent(Intent.ACTION_VIEW, uri))
} catch (e: Exception) {
Log.e(AppConfig.TAG, "Failed to open URI", e)
}
}
/**
@@ -296,7 +276,7 @@ object Utils {
return try {
UUID.randomUUID().toString().replace("-", "")
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to generate UUID", e)
""
}
}
@@ -311,7 +291,7 @@ object Utils {
return try {
URLDecoder.decode(url, Charsets.UTF_8.toString())
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to decode URL", e)
url
}
}
@@ -326,7 +306,7 @@ object Utils {
return try {
URLEncoder.encode(url, Charsets.UTF_8.toString()).replace("+", "%20")
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to encode URL", e)
url
}
}
@@ -339,13 +319,18 @@ object Utils {
* @return The content of the asset file as a string.
*/
fun readTextFromAssets(context: Context?, fileName: String): String {
if (context == null) {
return ""
if (context == null) return ""
return try {
context.assets.open(fileName).use { inputStream ->
inputStream.bufferedReader().use { reader ->
reader.readText()
}
}
} catch (e: Exception) {
Log.e(AppConfig.TAG, "Failed to read asset file: $fileName", e)
""
}
val content = context.assets.open(fileName).bufferedReader().use {
it.readText()
}
return content
}
/**
@@ -355,11 +340,15 @@ object Utils {
* @return The path to the user asset directory.
*/
fun userAssetPath(context: Context?): String {
if (context == null)
return ""
val extDir = context.getExternalFilesDir(AppConfig.DIR_ASSETS)
?: return context.getDir(AppConfig.DIR_ASSETS, 0).absolutePath
return extDir.absolutePath
if (context == null) return ""
return try {
context.getExternalFilesDir(AppConfig.DIR_ASSETS)?.absolutePath
?: context.getDir(AppConfig.DIR_ASSETS, 0).absolutePath
} catch (e: Exception) {
Log.e(AppConfig.TAG, "Failed to get user asset path", e)
""
}
}
/**
@@ -369,11 +358,15 @@ object Utils {
* @return The path to the backup directory.
*/
fun backupPath(context: Context?): String {
if (context == null)
return ""
val extDir = context.getExternalFilesDir(AppConfig.DIR_BACKUPS)
?: return context.getDir(AppConfig.DIR_BACKUPS, 0).absolutePath
return extDir.absolutePath
if (context == null) return ""
return try {
context.getExternalFilesDir(AppConfig.DIR_BACKUPS)?.absolutePath
?: context.getDir(AppConfig.DIR_BACKUPS, 0).absolutePath
} catch (e: Exception) {
Log.e(AppConfig.TAG, "Failed to get backup path", e)
""
}
}
/**
@@ -382,8 +375,13 @@ object Utils {
* @return The device ID for XUDP base key.
*/
fun getDeviceIdForXUDPBaseKey(): String {
val androidId = Settings.Secure.ANDROID_ID.toByteArray(Charsets.UTF_8)
return Base64.encodeToString(androidId.copyOf(32), Base64.NO_PADDING.or(Base64.URL_SAFE))
return try {
val androidId = Settings.Secure.ANDROID_ID.toByteArray(Charsets.UTF_8)
Base64.encodeToString(androidId.copyOf(32), Base64.NO_PADDING.or(Base64.URL_SAFE))
} catch (e: Exception) {
Log.e(AppConfig.TAG, "Failed to generate device ID", e)
""
}
}
/**
@@ -403,11 +401,10 @@ object Utils {
* @return The formatted IPv6 address, or the original address if not valid.
*/
fun getIpv6Address(address: String?): String {
if (address == null) {
return ""
}
if (address.isNullOrEmpty()) return ""
return if (isIpv6Address(address) && !address.contains('[') && !address.contains(']')) {
String.format("[%s]", address)
"[$address]"
} else {
address
}
@@ -431,8 +428,7 @@ object Utils {
* @return The URL string with illegal characters replaced.
*/
fun fixIllegalUrl(str: String): String {
return str
.replace(" ", "%20")
return str.replace(" ", "%20")
.replace("|", "%7C")
}
@@ -463,14 +459,15 @@ object Utils {
* @return True if the string is a valid subscription URL, false otherwise.
*/
fun isValidSubUrl(value: String?): Boolean {
try {
if (value.isNullOrEmpty()) return false
if (URLUtil.isHttpsUrl(value)) return true
if (URLUtil.isHttpUrl(value) && value.contains(LOOPBACK)) return true
if (value.isNullOrEmpty()) return false
return try {
URLUtil.isHttpsUrl(value) ||
(URLUtil.isHttpUrl(value) && value.contains(LOOPBACK))
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to validate subscription URL", e)
false
}
return false
}
/**
@@ -489,7 +486,7 @@ object Utils {
*
* @return True if the package is Xray, false otherwise.
*/
fun isXray(): Boolean = (ANG_PACKAGE.startsWith("com.v2ray.ang"))
fun isXray(): Boolean = ANG_PACKAGE.startsWith("com.v2ray.ang")
}
@@ -1,5 +1,7 @@
package com.v2ray.ang.util
import android.util.Log
import com.v2ray.ang.AppConfig
import java.io.BufferedOutputStream
import java.io.File
import java.io.FileInputStream
@@ -61,7 +63,7 @@ object ZipUtil {
zos.closeEntry()
zos.close()
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to zip folder", e)
return false
}
return true
@@ -97,7 +99,7 @@ object ZipUtil {
}
}
} catch (e: Exception) {
e.printStackTrace()
Log.e(AppConfig.TAG, "Failed to unzip file", e)
return false
}
return true
@@ -13,7 +13,6 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.v2ray.ang.AngApplication
import com.v2ray.ang.AppConfig
import com.v2ray.ang.AppConfig.ANG_PACKAGE
import com.v2ray.ang.R
import com.v2ray.ang.dto.ProfileItem
import com.v2ray.ang.dto.ServersCache
@@ -63,7 +62,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
getApplication<AngApplication>().unregisterReceiver(mMsgReceiver)
tcpingTestScope.coroutineContext[Job]?.cancelChildren()
SpeedtestManager.closeAllTcpSockets()
Log.i(ANG_PACKAGE, "Main ViewModel is cleared")
Log.i(AppConfig.TAG, "Main ViewModel is cleared")
super.onCleared()
}
@@ -26,7 +26,7 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
override fun onCleared() {
PreferenceManager.getDefaultSharedPreferences(getApplication())
.unregisterOnSharedPreferenceChangeListener(this)
Log.i(AppConfig.ANG_PACKAGE, "Settings ViewModel is cleared")
Log.i(AppConfig.TAG, "Settings ViewModel is cleared")
super.onCleared()
}
@@ -36,7 +36,7 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
* @param key The key of the changed preference.
*/
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String?) {
Log.d(AppConfig.ANG_PACKAGE, "Observe settings changed: $key")
Log.i(AppConfig.TAG, "Observe settings changed: $key")
when (key) {
AppConfig.PREF_MODE,
AppConfig.PREF_VPN_DNS,
@@ -265,7 +265,7 @@
<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_user_asset_setting">Asset files</string>
<string name="title_user_asset_setting">فایل های منبع جغرافیایی</string>
<string name="title_sort_by_test_results">مرتب‌ سازی بر اساس نتایج آزمایش</string>
<string name="title_filter_config">فیلتر کردن کانفیگ‌ها</string>
<string name="filter_config_all">همه گروه‌های اشتراک</string>
@@ -267,7 +267,7 @@
<string name="title_sub_update">Обновить подписку группы</string>
<string name="title_ping_all_server">Проверка профилей группы</string>
<string name="title_real_ping_all_server">Время отклика профилей группы</string>
<string name="title_user_asset_setting">Asset files</string>
<string name="title_user_asset_setting">Файлы ресурсов</string>
<string name="title_sort_by_test_results">Сортировка по результатам теста</string>
<string name="title_filter_config">Фильтр групп</string>
<string name="filter_config_all">Все группы</string>
+1 -1
View File
@@ -1,5 +1,5 @@
[versions]
agp = "8.9.0"
agp = "8.9.1"
desugar_jdk_libs = "2.1.5"
gradleLicensePlugin = "0.9.8"
kotlin = "2.1.20"