Compare commits

...

36 Commits

Author SHA1 Message Date
2dust 9cd5fefdca up 1.9.15 2024-11-10 09:58:08 +08:00
2dust 25c42c475f newFixedThreadPool(Runtime.getRuntime().availableProcessors()) 2024-11-09 19:38:59 +08:00
DecorativeFamily 96e66da071 Update persian translate (#3913)
* Update persian translate

Update persian translate

* Update persian translate 

Update persian translate
2024-11-09 18:31:36 +08:00
2dust 6914b9ee1b Bug fix
https://github.com/2dust/v2rayNG/issues/3911
2024-11-09 17:32:13 +08:00
TTG cfc6546c97 Update Configuration to Optimize (#3912)
* Remove geolocation-cn

* Segment and update DNS lists
2024-11-09 14:40:24 +08:00
2dust 0880313659 Bug fix
https://github.com/2dust/v2rayNG/issues/3885
2024-11-08 15:06:52 +08:00
2dust 875ca02126 Bug fix
https://github.com/2dust/v2rayNG/issues/3900
2024-11-08 10:43:13 +08:00
2dust c2d5925053 up 1.9.14 2024-11-07 20:54:09 +08:00
2dust 547bbf8e95 Bug fix 2024-11-07 20:37:17 +08:00
2dust 2218251b03 Bug fix
https://github.com/2dust/v2rayNG/issues/3895
2024-11-07 20:32:08 +08:00
2dust 4da3a23162 up 1.9.13 2024-11-06 14:31:10 +08:00
2dust 28a90baf88 Bug fix
https://github.com/2dust/v2rayNG/issues/3883
2024-11-06 11:22:01 +08:00
2dust 7bbdda2f2f This is a temporary solution
https://github.com/2dust/v2rayNG/issues/3883
2024-11-05 21:32:50 +08:00
2dust 884b444a41 up 1.9.12 2024-11-05 19:01:03 +08:00
2dust e1ff2df36e Bug fix 2024-11-05 18:59:05 +08:00
2dust 61c0111778 Revert "Refactor listenForPackageChanges to remove redundant registerReceiver calls (#3872)"
This reverts commit 5f167512f5.
2024-11-05 18:07:55 +08:00
Tamim Hossain bfc9a64e07 Correct isEmpty syntax for collection check (#3881)
Fixed the syntax for checking if `filesToCompress` is empty by using `isEmpty()` instead of `isEmpty`. This ensures correct functionality when verifying if the collection has elements.
2024-11-05 18:01:32 +08:00
Tamim Hossain 2ba92045cc Remove unnecessary Boolean comparisons in conditional checks (#3880)
Simplified conditional checks by removing unnecessary `== true` comparisons. The `decodeSettingsBool` function returns a non-nullable Boolean with a default value, so direct usage improves readability and keeps the code concise.
2024-11-05 18:00:52 +08:00
Tamim Hossain aeca9f51c8 Remove redundant Boolean comparison in runLoop call (#3879)
Simplified the call to `runLoop` by removing the redundant `== true` comparison. Since `decodeSettingsBool` returns a non-nullable Boolean, direct usage improves readability.
2024-11-05 18:00:23 +08:00
Tamim Hossain b107c0ac1d Remove redundant TAG field in ProcessService (#3878)
Refactored `ProcessService` by removing the redundant `TAG` variable and using `ANG_PACKAGE` directly in logging calls, simplifying the code and reducing unnecessary field assignments.
2024-11-05 17:59:37 +08:00
Tamim Hossain 65a04b4784 Refactor getString call to use orEmpty for null safety (#3877)
Updated `getString` call to use `orEmpty()` instead of specifying a default empty string, making the code cleaner and handling nullability more effectively.
2024-11-05 17:58:23 +08:00
Tamim Hossain eab9f50cfd Refactor BootReceiver for improved null handling and readability (#3876)
Refactored `BootReceiver` to simplify null checks and conditional structure. Combined context and intent checks into a single early return and refactored logic for `decodeStartOnBoot` and `getSelectServer` to improve readability.
2024-11-05 17:57:51 +08:00
Tamim Hossain b8bb83b524 Used safecall ? (#3874)
Used safecall `?`
2024-11-05 17:56:32 +08:00
Tamim Hossain 93eb9fe3b9 Introduce NetworkType enum to improve network type handling (#3873)
* Introduce NetworkType enum to improve network type handling

Created a `NetworkType` enum to represent various network types, improving readability and reducing potential errors caused by hardcoded string comparisons. Updated the `getQueryDic` function to utilize this enum.

* Refactor to use NetworkType enum in VmessFmt

Replaced hardcoded network type strings with the `NetworkType` enum in `VmessFmt` functions. Updated `parse`, `toUri`, and `parseVmessStd` methods to use `NetworkType.fromString`, improving readability and reducing errors caused by typos in network type strings.
2024-11-05 17:56:04 +08:00
Tamim Hossain 5f167512f5 Refactor listenForPackageChanges to remove redundant registerReceiver calls (#3872)
Refactored the `listenForPackageChanges` function to remove redundant calls to `registerReceiver` by creating a single `IntentFilter` instance. This simplifies the code and improves readability.
2024-11-05 17:55:08 +08:00
Tamim Hossain a727b81263 Update registerReceiver usage to comply with Android Tiramisu+ guidelines (#3871)
### Summary
- Updated `registerReceiver` usage to align with Android Tiramisu+ documentation.

### Details
- Replaced direct `registerReceiver` calls with `ContextCompat.registerReceiver` for improved compatibility.
- Used `RECEIVER_EXPORTED` and `RECEIVER_NOT_EXPORTED` flags based on API level to ensure correct receiver permissions.
- Added reference to the official Android documentation for `registerReceiver`.

### References
- [Documentation on registerReceiver](https://developer.android.com/reference/androidx/core/content/ContextCompat#registerReceiver(android.content.Context,android.content.BroadcastReceiver,android.content.IntentFilter,int))

This commit ensures that the `registerReceiver` call is consistent with the latest Android standards, improving compatibility and security across Android versions.
2024-11-05 16:42:17 +08:00
solokot f27c9192d1 Update Russian translation (#3870) 2024-11-05 16:39:19 +08:00
DecorativeFamily 90153fa17f Update persian translate (#3869)
* Update persian translate

* Update strings.xml
2024-11-05 16:38:54 +08:00
DecorativeFamily cdfaa01852 Revert "Update persian translate (#3867)" (#3868)
This reverts commit 33d2c3b00d.
2024-11-05 15:05:42 +08:00
DecorativeFamily 33d2c3b00d Update persian translate (#3867) 2024-11-05 15:03:35 +08:00
DecorativeFamily b7f992cdc0 Update gradle-wrapper.properties (#3844)
Gradle 8.10.2
2024-11-05 15:03:21 +08:00
DecorativeFamily 0a6a24e309 Gradle (#3839)
* Create dependabot.yml

* Update dependabot.yml

* Update libs.versions.toml

* Delete .github/dependabot.yml
2024-11-05 15:03:07 +08:00
2dust 153b4cffef Unable to obtain the notification permission 2024-11-05 14:29:44 +08:00
2dust f09a413232 Bug fix
https://github.com/2dust/v2rayNG/issues/3858
2024-11-05 10:24:06 +08:00
886963226 cba58f6ae2 Update V2rayConfigManager.kt (#3860) 2024-11-05 09:31:01 +08:00
2dust 2bf4b91488 Bug fix
https://github.com/2dust/v2rayNG/issues/3852
2024-11-04 20:22:40 +08:00
40 changed files with 376 additions and 166 deletions
+2 -2
View File
@@ -11,8 +11,8 @@ android {
applicationId = "com.v2ray.ang"
minSdk = 21
targetSdk = 35
versionCode = 605
versionName = "1.9.11"
versionCode = 611
versionName = "1.9.15"
multiDexEnabled = true
splits {
abi {
@@ -41,14 +41,89 @@
"geosite:private"
]
},
{
"remarks": "代理海外公共DNSIP",
"outboundTag": "proxy",
"ip": [
"1.1.1.1",
"1.0.0.1",
"2606:4700:4700::1111",
"2606:4700:4700::1001",
"1.1.1.2",
"1.0.0.2",
"2606:4700:4700::1112",
"2606:4700:4700::1002",
"1.1.1.3",
"1.0.0.3",
"2606:4700:4700::1113",
"2606:4700:4700::1003",
"8.8.8.8",
"8.8.4.4",
"2001:4860:4860::8888",
"2001:4860:4860::8844",
"94.140.14.14",
"94.140.15.15",
"2a10:50c0::ad1:ff",
"2a10:50c0::ad2:ff",
"94.140.14.15",
"94.140.15.16",
"2a10:50c0::bad1:ff",
"2a10:50c0::bad2:ff",
"94.140.14.140",
"94.140.14.141",
"2a10:50c0::1:ff",
"2a10:50c0::2:ff",
"208.67.222.222",
"208.67.220.220",
"2620:119:35::35",
"2620:119:53::53",
"208.67.222.123",
"208.67.220.123",
"2620:119:35::123",
"2620:119:53::123",
"9.9.9.9",
"149.112.112.112",
"2620:fe::9",
"2620:fe::fe",
"9.9.9.11",
"149.112.112.11",
"2620:fe::11",
"2620:fe::fe:11",
"9.9.9.10",
"149.112.112.10",
"2620:fe::10",
"2620:fe::fe:10",
"77.88.8.8",
"77.88.8.1",
"2a02:6b8::feed:0ff",
"2a02:6b8:0:1::feed:0ff",
"77.88.8.88",
"77.88.8.2",
"2a02:6b8::feed:bad",
"2a02:6b8:0:1::feed:bad",
"77.88.8.7",
"77.88.8.3",
"2a02:6b8::feed:a11",
"2a02:6b8:0:1::feed:a11"
]
},
{
"remarks": "代理海外公共DNS域名",
"outboundTag": "proxy",
"domain": [
"domain:cloudflare-dns.com",
"domain:one.one.one.one",
"domain:dns.google",
"domain:adguard-dns.com",
"domain:opendns.com",
"domain:quad9.net",
"domain:dns.yandex.ru"
]
},
{
"remarks": "代理IP",
"outboundTag": "proxy",
"ip": [
"1.0.0.1",
"1.1.1.1",
"8.8.8.8",
"8.8.4.4",
"geoip:facebook",
"geoip:fastly",
"geoip:google",
@@ -34,29 +34,60 @@
"geosite:private"
]
},
{
"remarks": "绕过中国公共DNSIP",
"outboundTag": "direct",
"ip": [
"223.5.5.5",
"223.6.6.6",
"2400:3200::1",
"2400:3200:baba::1",
"119.29.29.29",
"1.12.12.12",
"120.53.53.53",
"2402:4e00::",
"2402:4e00:1::",
"180.76.76.76",
"2400:da00::6666",
"114.114.114.114",
"114.114.115.115",
"114.114.114.119",
"114.114.115.119",
"114.114.114.110",
"114.114.115.110",
"180.184.1.1",
"180.184.2.2",
"101.226.4.6",
"218.30.118.6",
"123.125.81.6",
"140.207.198.6",
"1.2.4.8",
"210.2.4.8",
"117.50.11.11",
"52.80.66.66",
"2400:7fc0:849e:200::4",
"2404:c2c0:85d8:901::4",
"117.50.10.10",
"52.80.52.52",
"2400:7fc0:849e:200::8",
"2404:c2c0:85d8:901::8"
]
},
{
"remarks": "绕过中国公共DNS域名",
"outboundTag": "direct",
"domain": [
"domain:alidns.com",
"domain:doh.pub",
"domain:dot.pub",
"domain:360.cn",
"domain:onedns.net"
]
},
{
"remarks": "绕过中国IP",
"outboundTag": "direct",
"ip": [
"223.5.5.5/32",
"223.6.6.6/32",
"2400:3200::1/128",
"2400:3200:baba::1/128",
"119.29.29.29/32",
"1.12.12.12/32",
"120.53.53.53/32",
"2402:4e00::/128",
"2402:4e00:1::/128",
"180.76.76.76/32",
"2400:da00::6666/128",
"114.114.114.114/32",
"114.114.115.115/32",
"180.184.1.1/32",
"180.184.2.2/32",
"101.226.4.6/32",
"218.30.118.6/32",
"123.125.81.6/32",
"140.207.198.6/32",
"geoip:cn"
]
},
@@ -64,13 +95,7 @@
"remarks": "绕过中国域名",
"outboundTag": "direct",
"domain": [
"domain:dns.alidns.com",
"domain:doh.pub",
"domain:dot.pub",
"domain:doh.360.cn",
"domain:dot.360.cn",
"geosite:cn",
"geosite:geolocation-cn"
"geosite:cn"
]
},
{
@@ -0,0 +1,17 @@
package com.v2ray.ang.dto
enum class NetworkType(val type: String) {
TCP("tcp"),
KCP("kcp"),
WS("ws"),
HTTP_UPGRADE("httpupgrade"),
SPLIT_HTTP("splithttp"),
HTTP("http"),
H2("h2"),
QUIC("quic"),
GRPC("grpc");
companion object {
fun fromString(type: String?) = entries.find { it.type == type } ?: TCP
}
}
@@ -470,16 +470,16 @@ data class V2rayConfig(
if (protocol.equals(EConfigType.VMESS.name, true)
|| protocol.equals(EConfigType.VLESS.name, true)
) {
return settings?.vnext?.get(0)?.address
return settings?.vnext?.first()?.address
} else if (protocol.equals(EConfigType.SHADOWSOCKS.name, true)
|| protocol.equals(EConfigType.SOCKS.name, true)
|| protocol.equals(EConfigType.HTTP.name, true)
|| protocol.equals(EConfigType.TROJAN.name, true)
|| protocol.equals(EConfigType.HYSTERIA2.name, true)
) {
return settings?.servers?.get(0)?.address
return settings?.servers?.first()?.address
} else if (protocol.equals(EConfigType.WIREGUARD.name, true)) {
return settings?.peers?.get(0)?.endpoint?.substringBeforeLast(":")
return settings?.peers?.first()?.endpoint?.substringBeforeLast(":")
}
return null
}
@@ -488,16 +488,16 @@ data class V2rayConfig(
if (protocol.equals(EConfigType.VMESS.name, true)
|| protocol.equals(EConfigType.VLESS.name, true)
) {
return settings?.vnext?.get(0)?.port
return settings?.vnext?.first()?.port
} else if (protocol.equals(EConfigType.SHADOWSOCKS.name, true)
|| protocol.equals(EConfigType.SOCKS.name, true)
|| protocol.equals(EConfigType.HTTP.name, true)
|| protocol.equals(EConfigType.TROJAN.name, true)
|| protocol.equals(EConfigType.HYSTERIA2.name, true)
) {
return settings?.servers?.get(0)?.port
return settings?.servers?.first()?.port
} else if (protocol.equals(EConfigType.WIREGUARD.name, true)) {
return settings?.peers?.get(0)?.endpoint?.substringAfterLast(":")?.toInt()
return settings?.peers?.first()?.endpoint?.substringAfterLast(":")?.toInt()
}
return null
}
@@ -512,16 +512,16 @@ data class V2rayConfig(
if (protocol.equals(EConfigType.VMESS.name, true)
|| protocol.equals(EConfigType.VLESS.name, true)
) {
return settings?.vnext?.get(0)?.users?.get(0)?.id
return settings?.vnext?.first()?.users?.first()?.id
} else if (protocol.equals(EConfigType.SHADOWSOCKS.name, true)
|| protocol.equals(EConfigType.TROJAN.name, true)
|| protocol.equals(EConfigType.HYSTERIA2.name, true)
) {
return settings?.servers?.get(0)?.password
return settings?.servers?.first()?.password
} else if (protocol.equals(EConfigType.SOCKS.name, true)
|| protocol.equals(EConfigType.HTTP.name, true)
) {
return settings?.servers?.get(0)?.users?.get(0)?.pass
return settings?.servers?.first()?.users?.first()?.pass
} else if (protocol.equals(EConfigType.WIREGUARD.name, true)) {
return settings?.secretKey
}
@@ -530,9 +530,9 @@ data class V2rayConfig(
fun getSecurityEncryption(): String? {
return when {
protocol.equals(EConfigType.VMESS.name, true) -> settings?.vnext?.get(0)?.users?.get(0)?.security
protocol.equals(EConfigType.VLESS.name, true) -> settings?.vnext?.get(0)?.users?.get(0)?.encryption
protocol.equals(EConfigType.SHADOWSOCKS.name, true) -> settings?.servers?.get(0)?.method
protocol.equals(EConfigType.VMESS.name, true) -> settings?.vnext?.first()?.users?.first()?.security
protocol.equals(EConfigType.VLESS.name, true) -> settings?.vnext?.first()?.users?.first()?.encryption
protocol.equals(EConfigType.SHADOWSOCKS.name, true) -> settings?.servers?.first()?.method
else -> null
}
}
@@ -1,6 +1,7 @@
package com.v2ray.ang.fmt
import com.v2ray.ang.AppConfig
import com.v2ray.ang.dto.NetworkType
import com.v2ray.ang.dto.ProfileItem
import com.v2ray.ang.extension.isNotNullEmpty
import com.v2ray.ang.util.Utils
@@ -31,7 +32,6 @@ open class FmtBase {
fun getQueryDic(config: ProfileItem): HashMap<String, String> {
val dicQuery = HashMap<String, String>()
dicQuery["security"] = config.security?.ifEmpty { "none" }.orEmpty()
config.sni.let { if (it.isNotNullEmpty()) dicQuery["sni"] = it.orEmpty() }
config.alpn.let { if (it.isNotNullEmpty()) dicQuery["alpn"] = it.orEmpty() }
@@ -41,37 +41,38 @@ open class FmtBase {
config.spiderX.let { if (it.isNotNullEmpty()) dicQuery["spx"] = it.orEmpty() }
config.flow.let { if (it.isNotNullEmpty()) dicQuery["flow"] = it.orEmpty() }
dicQuery["type"] = config.network?.ifEmpty { AppConfig.DEFAULT_NETWORK }.orEmpty()
val networkType = NetworkType.fromString(config.network)
dicQuery["type"] = networkType.type
when (config.network) {
"tcp" -> {
when (networkType) {
NetworkType.TCP -> {
dicQuery["headerType"] = config.headerType?.ifEmpty { "none" }.orEmpty()
config.host.let { if (it.isNotNullEmpty()) dicQuery["host"] = it.orEmpty() }
}
"kcp" -> {
NetworkType.KCP -> {
dicQuery["headerType"] = config.headerType?.ifEmpty { "none" }.orEmpty()
config.seed.let { if (it.isNotNullEmpty()) dicQuery["seed"] = it.orEmpty() }
}
"ws", "httpupgrade", "splithttp" -> {
NetworkType.WS, NetworkType.HTTP_UPGRADE, NetworkType.SPLIT_HTTP -> {
config.host.let { if (it.isNotNullEmpty()) dicQuery["host"] = it.orEmpty() }
config.path.let { if (it.isNotNullEmpty()) dicQuery["path"] = it.orEmpty() }
}
"http", "h2" -> {
NetworkType.HTTP, NetworkType.H2 -> {
dicQuery["type"] = "http"
config.host.let { if (it.isNotNullEmpty()) dicQuery["host"] = it.orEmpty() }
config.path.let { if (it.isNotNullEmpty()) dicQuery["path"] = it.orEmpty() }
}
"quic" -> {
NetworkType.QUIC -> {
dicQuery["headerType"] = config.headerType?.ifEmpty { "none" }.orEmpty()
config.quicSecurity.let { if (it.isNotNullEmpty()) dicQuery["quicSecurity"] = it.orEmpty() }
config.quicKey.let { if (it.isNotNullEmpty()) dicQuery["key"] = it.orEmpty() }
}
"grpc" -> {
NetworkType.GRPC -> {
config.mode.let { if (it.isNotNullEmpty()) dicQuery["mode"] = it.orEmpty() }
config.authority.let { if (it.isNotNullEmpty()) dicQuery["authority"] = it.orEmpty() }
config.serviceName.let { if (it.isNotNullEmpty()) dicQuery["serviceName"] = it.orEmpty() }
@@ -10,7 +10,7 @@ object HttpFmt : FmtBase() {
fun toOutbound(profileItem: ProfileItem): OutboundBean? {
val outboundBean = OutboundBean.create(EConfigType.HTTP)
outboundBean?.settings?.servers?.get(0)?.let { server ->
outboundBean?.settings?.servers?.first()?.let { server ->
server.address = profileItem.server.orEmpty()
server.port = profileItem.serverPort.orEmpty().toInt()
if (profileItem.username.isNotNullEmpty()) {
@@ -9,19 +9,25 @@ import java.net.URI
object ShadowsocksFmt : FmtBase() {
fun parse(str: String): ProfileItem? {
return parseSip002(str) ?: parseLegacy(str)
}
fun parseSip002(str: String): ProfileItem? {
val config = ProfileItem.create(EConfigType.SHADOWSOCKS)
val uri = URI(Utils.fixIllegalUrl(str))
if (uri.idnHost.isEmpty() || uri.userInfo.isEmpty()) return null
if (uri.idnHost.isEmpty()) return null
if (uri.port <= 0) return null
if (uri.userInfo.isNullOrEmpty()) return null
config.remarks = Utils.urlDecode(uri.fragment.orEmpty())
config.server = uri.idnHost
config.serverPort = uri.port.toString()
val result = if (uri.userInfo.contains(":")) {
uri.userInfo.split(":")
uri.userInfo.split(":", limit = 2)
} else {
Utils.decode(uri.userInfo).split(":")
Utils.decode(uri.userInfo).split(":", limit = 2)
}
if (result.count() == 2) {
config.method = result.first()
@@ -42,16 +48,53 @@ object ShadowsocksFmt : FmtBase() {
return config
}
fun parseLegacy(str: String): ProfileItem? {
val config = ProfileItem.create(EConfigType.SHADOWSOCKS)
var result = str.replace(EConfigType.SHADOWSOCKS.protocolScheme, "")
val indexSplit = result.indexOf("#")
if (indexSplit > 0) {
try {
config.remarks =
Utils.urlDecode(result.substring(indexSplit + 1, result.length))
} catch (e: Exception) {
e.printStackTrace()
}
result = result.substring(0, indexSplit)
}
//part decode
val indexS = result.indexOf("@")
result = if (indexS > 0) {
Utils.decode(result.substring(0, indexS)) + result.substring(
indexS,
result.length
)
} else {
Utils.decode(result)
}
val legacyPattern = "^(.+?):(.*)@(.+?):(\\d+?)/?$".toRegex()
val match = legacyPattern.matchEntire(result) ?: return null
config.server = match.groupValues[3].removeSurrounding("[", "]")
config.serverPort = match.groupValues[4]
config.password = match.groupValues[2]
config.method = match.groupValues[1].lowercase()
return config
}
fun toUri(config: ProfileItem): String {
val pw = "${config.method}:${config.password}"
return toUri(config, pw, null)
return toUri(config, Utils.encode(pw), null)
}
fun toOutbound(profileItem: ProfileItem): OutboundBean? {
val outboundBean = OutboundBean.create(EConfigType.SHADOWSOCKS)
outboundBean?.settings?.servers?.get(0)?.let { server ->
outboundBean?.settings?.servers?.first()?.let { server ->
server.address = profileItem.server.orEmpty()
server.port = profileItem.serverPort.orEmpty().toInt()
server.password = profileItem.password
@@ -15,13 +15,14 @@ object SocksFmt : FmtBase() {
val uri = URI(Utils.fixIllegalUrl(str))
if (uri.idnHost.isEmpty()) return null
if (uri.port <= 0) return null
config.remarks = Utils.urlDecode(uri.fragment.orEmpty())
config.server = uri.idnHost
config.serverPort = uri.port.toString()
if (uri.userInfo?.isEmpty() == false) {
val result = Utils.decode(uri.userInfo).split(":")
val result = Utils.decode(uri.userInfo).split(":", limit = 2)
if (result.count() == 2) {
config.username = result.first()
config.password = result.last()
@@ -38,13 +39,13 @@ object SocksFmt : FmtBase() {
else
":"
return toUri(config, pw, null)
return toUri(config, Utils.encode(pw), null)
}
fun toOutbound(profileItem: ProfileItem): OutboundBean? {
val outboundBean = OutboundBean.create(EConfigType.SOCKS)
outboundBean?.settings?.servers?.get(0)?.let { server ->
outboundBean?.settings?.servers?.first()?.let { server ->
server.address = profileItem.server.orEmpty()
server.port = profileItem.serverPort.orEmpty().toInt()
if (profileItem.username.isNotNullEmpty()) {
@@ -67,7 +67,7 @@ object TrojanFmt : FmtBase() {
fun toOutbound(profileItem: ProfileItem): OutboundBean? {
val outboundBean = OutboundBean.create(EConfigType.TROJAN)
outboundBean?.settings?.servers?.get(0)?.let { server ->
outboundBean?.settings?.servers?.first()?.let { server ->
server.address = profileItem.server.orEmpty()
server.port = profileItem.serverPort.orEmpty().toInt()
server.password = profileItem.password
@@ -65,7 +65,7 @@ object VlessFmt : FmtBase() {
fun toOutbound(profileItem: ProfileItem): OutboundBean? {
val outboundBean = OutboundBean.create(EConfigType.VLESS)
outboundBean?.settings?.vnext?.get(0)?.let { vnext ->
outboundBean?.settings?.vnext?.first()?.let { vnext ->
vnext.address = profileItem.server.orEmpty()
vnext.port = profileItem.serverPort.orEmpty().toInt()
vnext.users[0].id = profileItem.password.orEmpty()
@@ -4,6 +4,7 @@ import android.text.TextUtils
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
import com.v2ray.ang.dto.V2rayConfig.OutboundBean
import com.v2ray.ang.dto.VmessQRCode
@@ -52,22 +53,24 @@ object VmessFmt : FmtBase() {
config.host = vmessQRCode.host
config.path = vmessQRCode.path
when (config.network) {
"kcp" -> {
when (NetworkType.fromString(config.network)) {
NetworkType.KCP -> {
config.seed = vmessQRCode.path
}
"quic" -> {
NetworkType.QUIC -> {
config.quicSecurity = vmessQRCode.host
config.quicKey = vmessQRCode.path
}
"grpc" -> {
NetworkType.GRPC -> {
config.mode = vmessQRCode.type
config.serviceName = vmessQRCode.path
config.authority = vmessQRCode.host
}
else -> {}
}
config.security = vmessQRCode.tls
config.insecure = allowInsecure
config.sni = vmessQRCode.sni
@@ -90,22 +93,24 @@ object VmessFmt : FmtBase() {
vmessQRCode.net = config.network.orEmpty()
vmessQRCode.type = config.headerType.orEmpty()
when (config.network) {
"kcp" -> {
when (NetworkType.fromString(config.network)) {
NetworkType.KCP -> {
vmessQRCode.path = config.seed.orEmpty()
}
"quic" -> {
NetworkType.QUIC -> {
vmessQRCode.host = config.quicSecurity.orEmpty()
vmessQRCode.path = config.quicKey.orEmpty()
}
"grpc" -> {
NetworkType.GRPC -> {
vmessQRCode.type = config.mode.orEmpty()
vmessQRCode.path = config.serviceName.orEmpty()
vmessQRCode.host = config.authority.orEmpty()
}
else -> {}
}
config.host.let { if (it.isNotNullEmpty()) vmessQRCode.host = it.orEmpty() }
config.path.let { if (it.isNotNullEmpty()) vmessQRCode.path = it.orEmpty() }
@@ -119,7 +124,7 @@ object VmessFmt : FmtBase() {
}
fun parseVmessStd(str: String): ProfileItem? {
var allowInsecure = MmkvManager.decodeSettingsBool(AppConfig.PREF_ALLOW_INSECURE, false)
val allowInsecure = MmkvManager.decodeSettingsBool(AppConfig.PREF_ALLOW_INSECURE, false)
val config = ProfileItem.create(EConfigType.VMESS)
val uri = URI(Utils.fixIllegalUrl(str))
@@ -132,7 +137,7 @@ object VmessFmt : FmtBase() {
config.password = uri.userInfo
config.method = AppConfig.DEFAULT_SECURITY
config.network = queryParam["type"] ?: "tcp"
config.network = NetworkType.fromString(queryParam["type"]).name
config.headerType = queryParam["headerType"]
config.host = queryParam["host"]
config.path = queryParam["path"]
@@ -157,7 +162,7 @@ object VmessFmt : FmtBase() {
fun toOutbound(profileItem: ProfileItem): OutboundBean? {
val outboundBean = OutboundBean.create(EConfigType.VMESS)
outboundBean?.settings?.vnext?.get(0)?.let { vnext ->
outboundBean?.settings?.vnext?.first()?.let { vnext ->
vnext.address = profileItem.server.orEmpty()
vnext.port = profileItem.serverPort.orEmpty().toInt()
vnext.users[0].id = profileItem.password.orEmpty()
@@ -82,8 +82,8 @@ object WireguardFmt : FmtBase() {
outboundBean?.settings?.let { wireguard ->
wireguard.secretKey = profileItem.secretKey
wireguard.address = (profileItem.localAddress ?: WIREGUARD_LOCAL_ADDRESS_V4).split(",")
wireguard.peers?.get(0)?.publicKey = profileItem.publicKey.orEmpty()
wireguard.peers?.get(0)?.endpoint = Utils.getIpv6Address(profileItem.server) + ":${profileItem.serverPort}"
wireguard.peers?.first()?.publicKey = profileItem.publicKey.orEmpty()
wireguard.peers?.first()?.endpoint = Utils.getIpv6Address(profileItem.server) + ":${profileItem.serverPort}"
wireguard.mtu = profileItem.mtu?.toInt()
wireguard.reserved = profileItem.reserved?.split(",")?.map { it.toInt() }
}
@@ -277,7 +277,7 @@ object AngConfigManager {
if (serverList.isNotEmpty()) {
var count = 0
for (srv in serverList.reversed()) {
val config = CustomFmt.parse(server) ?: continue
val config = CustomFmt.parse(JsonUtil.toJson(srv)) ?: continue
config.subscriptionId = subid
val key = MmkvManager.encodeServerConfig("", config)
MmkvManager.encodeServerRaw(key, JsonUtil.toJsonPretty(srv))
@@ -19,7 +19,7 @@ object MigrateManager {
if (serverStorage.count().toInt() == 0) {
return false
}
var serverList = serverStorage.allKeys() ?: return false
val serverList = serverStorage.allKeys() ?: return false
Log.d(ANG_PACKAGE, "migrateServerConfig2Profile-" + serverList.count())
for (guid in serverList) {
@@ -36,7 +36,7 @@ object MigrateManager {
//check and remove old
decodeServerConfig(guid) ?: continue
//serverStorage.remove(guid)
serverStorage.remove(guid)
Log.d(ANG_PACKAGE, "migrateServerConfig2Profile-" + config.remarks)
}
Log.d(ANG_PACKAGE, "migrateServerConfig2Profile-end")
@@ -70,10 +70,10 @@ object MigrateManager {
config.serverPort = outbound.getServerPort().toString()
config.method = outbound.getSecurityEncryption()
config.password = outbound.getPassword()
config.flow = outbound?.settings?.vnext?.get(0)?.users[0]?.flow ?: outbound?.settings?.servers?.get(0)?.flow
config.flow = outbound?.settings?.vnext?.first()?.users?.first()?.flow ?: outbound?.settings?.servers?.first()?.flow
config.network = outbound?.streamSettings?.network ?: "tcp"
outbound.getTransportSettingDetails()?.let { transportDetails ->
config.network = "tcp"
config.headerType = transportDetails[0].orEmpty()
config.host = transportDetails[1].orEmpty()
config.path = transportDetails[2].orEmpty()
@@ -107,7 +107,7 @@ object MigrateManager {
config.remarks = configOld.remarks
config.server = outbound.getServerAddress()
config.serverPort = outbound.getServerPort().toString()
config.username = outbound.settings?.servers?.get(0)?.users?.get(0)?.user
config.username = outbound.settings?.servers?.first()?.users?.first()?.user
config.password = outbound.getPassword()
return config
@@ -120,7 +120,7 @@ object MigrateManager {
config.remarks = configOld.remarks
config.server = outbound.getServerAddress()
config.serverPort = outbound.getServerPort().toString()
config.username = outbound.settings?.servers?.get(0)?.users?.get(0)?.user
config.username = outbound.settings?.servers?.first()?.users?.first()?.user
config.password = outbound.getPassword()
return config
@@ -336,7 +336,7 @@ object MmkvManager {
}
fun decodeSettingsBool(key: String): Boolean {
return settingsStorage.decodeBool(key)
return settingsStorage.decodeBool(key,false)
}
fun decodeSettingsBool(key: String, defaultValue: Boolean): Boolean {
@@ -419,7 +419,7 @@ object V2rayConfigManager {
) {
muxEnabled = false
} else if (protocol.equals(EConfigType.VLESS.name, true)
&& outbound.settings?.vnext?.get(0)?.users?.get(0)?.flow?.isNotEmpty() == true
&& outbound.settings?.vnext?.first()?.users?.first()?.flow?.isNotEmpty() == true
) {
muxEnabled = false
}
@@ -455,7 +455,7 @@ object V2rayConfigManager {
val host = outbound.streamSettings?.tcpSettings?.header?.request?.headers?.Host
val requestString: String by lazy {
"""{"version":"1.1","method":"GET","headers":{"User-Agent":"Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.122 Mobile Safari/537.36"Safari/537.36,"Accept-Encoding":["gzip, deflate"],"Connection":["keep-alive"],"Pragma":"no-cache"}}"""
"""{"version":"1.1","method":"GET","headers":{"User-Agent":["Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.122 Mobile Safari/537.36"],"Accept-Encoding":["gzip, deflate"],"Connection":["keep-alive"],"Pragma":"no-cache"}}"""
}
outbound.streamSettings?.tcpSettings?.header?.request = JsonUtil.fromJson(
requestString,
@@ -8,11 +8,11 @@ import com.v2ray.ang.service.V2RayServiceManager
class BootReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (Intent.ACTION_BOOT_COMPLETED == intent?.action && MmkvManager.decodeStartOnBoot()) {
if (MmkvManager.getSelectServer().isNullOrEmpty()) {
return
}
V2RayServiceManager.startV2Ray(context!!)
}
//Check if context is not null and action is the one we want
if (context == null || intent?.action != Intent.ACTION_BOOT_COMPLETED) return
//Check if flag is true and a server is selected
if (!MmkvManager.decodeStartOnBoot() || MmkvManager.getSelectServer().isNullOrEmpty()) return
//Start v2ray
V2RayServiceManager.startV2Ray(context)
}
}
}
@@ -16,9 +16,9 @@ class TaskerReceiver : BroadcastReceiver() {
try {
val bundle = intent?.getBundleExtra(AppConfig.TASKER_EXTRA_BUNDLE)
val switch = bundle?.getBoolean(AppConfig.TASKER_EXTRA_BUNDLE_SWITCH, false)
val guid = bundle?.getString(AppConfig.TASKER_EXTRA_BUNDLE_GUID, "")
val guid = bundle?.getString(AppConfig.TASKER_EXTRA_BUNDLE_GUID).orEmpty()
if (switch == null || guid == null || TextUtils.isEmpty(guid)) {
if (switch == null || TextUtils.isEmpty(guid)) {
return
} else if (switch) {
if (guid == AppConfig.TASKER_DEFAULT_GUID) {
@@ -8,11 +8,10 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class ProcessService {
private val TAG = ANG_PACKAGE
private var process: Process? = null
fun runProcess(context: Context, cmd: MutableList<String>) {
Log.d(TAG, cmd.toString())
Log.d(ANG_PACKAGE, cmd.toString())
try {
val proBuilder = ProcessBuilder(cmd)
@@ -23,23 +22,23 @@ class ProcessService {
CoroutineScope(Dispatchers.IO).launch {
Thread.sleep(50L)
Log.d(TAG, "runProcess check")
Log.d(ANG_PACKAGE, "runProcess check")
process?.waitFor()
Log.d(TAG, "runProcess exited")
Log.d(ANG_PACKAGE, "runProcess exited")
}
Log.d(TAG, process.toString())
Log.d(ANG_PACKAGE, process.toString())
} catch (e: Exception) {
Log.d(TAG, e.toString())
Log.d(ANG_PACKAGE, e.toString())
}
}
fun stopProcess() {
try {
Log.d(TAG, "runProcess destroy")
Log.d(ANG_PACKAGE, "runProcess destroy")
process?.destroy()
} catch (e: Exception) {
Log.d(TAG, e.toString())
Log.d(ANG_PACKAGE, e.toString())
}
}
}
@@ -9,6 +9,7 @@ import android.graphics.drawable.Icon
import android.os.Build
import android.service.quicksettings.Tile
import android.service.quicksettings.TileService
import androidx.core.content.ContextCompat
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
import com.v2ray.ang.util.MessageUtil
@@ -32,24 +33,31 @@ class QSTileService : TileService() {
qsTile?.updateTile()
}
/**
* Refer to the official documentation for [registerReceiver](https://developer.android.com/reference/androidx/core/content/ContextCompat#registerReceiver(android.content.Context,android.content.BroadcastReceiver,android.content.IntentFilter,int):
* `registerReceiver(Context, BroadcastReceiver, IntentFilter, int)`.
*/
override fun onStartListening() {
super.onStartListening()
setState(Tile.STATE_INACTIVE)
mMsgReceive = ReceiveMessageHandler(this)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
registerReceiver(mMsgReceive, IntentFilter(AppConfig.BROADCAST_ACTION_ACTIVITY), RECEIVER_EXPORTED)
} else {
registerReceiver(mMsgReceive, IntentFilter(AppConfig.BROADCAST_ACTION_ACTIVITY))
}
val mFilter = IntentFilter(AppConfig.BROADCAST_ACTION_ACTIVITY)
ContextCompat.registerReceiver(applicationContext, mMsgReceive, mFilter, Utils.receiverFlags())
MessageUtil.sendMsg2Service(this, AppConfig.MSG_REGISTER_CLIENT, "")
}
override fun onStopListening() {
super.onStopListening()
unregisterReceiver(mMsgReceive)
mMsgReceive = null
try {
applicationContext.unregisterReceiver(mMsgReceive)
mMsgReceive = null
} catch (e: Exception) {
e.printStackTrace()
}
}
override fun onClick() {
@@ -13,6 +13,7 @@ import android.os.Build
import android.util.Log
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat
import com.v2ray.ang.AppConfig
import com.v2ray.ang.AppConfig.ANG_PACKAGE
import com.v2ray.ang.AppConfig.TAG_DIRECT
@@ -126,6 +127,11 @@ object V2RayServiceManager {
}
}
/**
* Refer to the official documentation for [registerReceiver](https://developer.android.com/reference/androidx/core/content/ContextCompat#registerReceiver(android.content.Context,android.content.BroadcastReceiver,android.content.IntentFilter,int):
* `registerReceiver(Context, BroadcastReceiver, IntentFilter, int)`.
*/
fun startV2rayPoint() {
val service = serviceControl?.get()?.getService() ?: return
val guid = MmkvManager.getSelectServer() ?: return
@@ -142,11 +148,7 @@ object V2RayServiceManager {
mFilter.addAction(Intent.ACTION_SCREEN_ON)
mFilter.addAction(Intent.ACTION_SCREEN_OFF)
mFilter.addAction(Intent.ACTION_USER_PRESENT)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
service.registerReceiver(mMsgReceive, mFilter, Context.RECEIVER_EXPORTED)
} else {
service.registerReceiver(mMsgReceive, mFilter)
}
ContextCompat.registerReceiver(service, mMsgReceive, mFilter, Utils.receiverFlags())
} catch (e: Exception) {
Log.d(ANG_PACKAGE, e.toString())
}
@@ -156,7 +158,7 @@ object V2RayServiceManager {
currentConfig = config
try {
v2rayPoint.runLoop(MmkvManager.decodeSettingsBool(AppConfig.PREF_PREFER_IPV6) == true)
v2rayPoint.runLoop(MmkvManager.decodeSettingsBool(AppConfig.PREF_PREFER_IPV6))
} catch (e: Exception) {
Log.d(ANG_PACKAGE, e.toString())
}
@@ -24,7 +24,7 @@ import libv2ray.Libv2ray
import java.util.concurrent.Executors
class V2RayTestService : Service() {
private val realTestScope by lazy { CoroutineScope(Executors.newFixedThreadPool(10).asCoroutineDispatcher()) }
private val realTestScope by lazy { CoroutineScope(Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()).asCoroutineDispatcher()) }
override fun onCreate() {
super.onCreate()
@@ -17,6 +17,7 @@ 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
@@ -153,9 +154,9 @@ class V2RayVpnService : VpnService(), ServiceControl {
builder.setSession(V2RayServiceManager.currentConfig?.remarks.orEmpty())
val selfPackageName = BuildConfig.APPLICATION_ID
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_PER_APP_PROXY) == true) {
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_PER_APP_PROXY)) {
val apps = MmkvManager.decodeSettingsStringSet(AppConfig.PREF_PER_APP_PROXY_SET)
val bypassApps = MmkvManager.decodeSettingsBool(AppConfig.PREF_BYPASS_APPS) == true
val bypassApps = MmkvManager.decodeSettingsBool(AppConfig.PREF_BYPASS_APPS)
//process self package
if (bypassApps) apps?.add(selfPackageName) else apps?.remove(selfPackageName)
apps?.forEach {
@@ -165,6 +166,7 @@ class V2RayVpnService : VpnService(), ServiceControl {
else
builder.addAllowedApplication(it)
} catch (e: PackageManager.NameNotFoundException) {
Log.d(ANG_PACKAGE, "setup error : --${e.localizedMessage}")
}
}
} else {
@@ -215,11 +217,11 @@ class V2RayVpnService : VpnService(), ServiceControl {
"--loglevel", "notice"
)
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_PREFER_IPV6) == true) {
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_PREFER_IPV6)) {
cmd.add("--netif-ip6addr")
cmd.add(PRIVATE_VLAN6_ROUTER)
}
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_LOCAL_DNS_ENABLED) == true) {
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_LOCAL_DNS_ENABLED)) {
val localDnsPort = Utils.parseInt(MmkvManager.decodeSettingsString(AppConfig.PREF_LOCAL_DNS_PORT), AppConfig.PORT_LOCAL_DNS.toInt())
cmd.add("--dnsgw")
cmd.add("$LOOPBACK:${localDnsPort}")
@@ -232,7 +234,7 @@ class V2RayVpnService : VpnService(), ServiceControl {
process = proBuilder
.directory(applicationContext.filesDir)
.start()
Thread(Runnable {
Thread {
Log.d(packageName, "$TUN2SOCKS check")
process.waitFor()
Log.d(packageName, "$TUN2SOCKS exited")
@@ -240,7 +242,7 @@ class V2RayVpnService : VpnService(), ServiceControl {
Log.d(packageName, "$TUN2SOCKS restart")
runTun2socks()
}
}).start()
}.start()
Log.d(packageName, process.toString())
sendFd()
@@ -133,7 +133,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
.request(Manifest.permission.POST_NOTIFICATIONS)
.subscribe {
if (!it)
toast(R.string.toast_permission_denied)
toast(R.string.toast_permission_denied_notification)
}
}
@@ -153,15 +153,32 @@ class ServerActivity : BaseActivity() {
sp_header_type_title?.text = if (networks[position] == "grpc")
getString(R.string.server_lab_mode_type) else
getString(R.string.server_lab_head_type)
config?.headerType?.let { it ->
sp_header_type?.setSelection(Utils.arrayFind(types, it))
}
config?.host?.let { it ->
et_request_host?.text = Utils.getEditable(it)
}
config?.path?.let { it ->
et_path?.text = Utils.getEditable(it)
}
sp_header_type?.setSelection(
Utils.arrayFind(
types,
when (networks[position]) {
"grpc" -> config?.mode
else -> config?.headerType
}.orEmpty()
)
)
et_request_host?.text = Utils.getEditable(
when (networks[position]) {
"quic" -> config?.quicSecurity
"grpc" -> config?.authority
else -> config?.host
}.orEmpty()
)
et_path?.text = Utils.getEditable(
when (networks[position]) {
"kcp" -> config?.seed
"quic" -> config?.quicKey
"grpc" -> config?.serviceName
else -> config?.path
}.orEmpty()
)
tv_request_host?.text = Utils.getEditable(
getString(
when (networks[position]) {
@@ -12,10 +12,9 @@ import com.v2ray.ang.R
import com.v2ray.ang.databinding.ActivityServerCustomConfigBinding
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.dto.ProfileItem
import com.v2ray.ang.dto.V2rayConfig
import com.v2ray.ang.extension.toast
import com.v2ray.ang.fmt.CustomFmt
import com.v2ray.ang.handler.MmkvManager
import com.v2ray.ang.util.JsonUtil
import com.v2ray.ang.util.Utils
import me.drakeet.support.toast.ToastCompat
@@ -75,8 +74,8 @@ class ServerCustomConfigActivity : BaseActivity() {
return false
}
val v2rayConfig = try {
JsonUtil.fromJson(binding.editor.text.toString(), V2rayConfig::class.java)
val profileItem = try {
CustomFmt.parse(binding.editor.text.toString())
} catch (e: Exception) {
e.printStackTrace()
ToastCompat.makeText(this, "${getString(R.string.toast_malformed_josn)} ${e.cause?.message}", Toast.LENGTH_LONG).show()
@@ -84,7 +83,11 @@ class ServerCustomConfigActivity : BaseActivity() {
}
val config = MmkvManager.decodeServerConfig(editGuid) ?: ProfileItem.create(EConfigType.CUSTOM)
config.remarks = if (binding.etRemarks.text.isNullOrEmpty()) v2rayConfig.remarks.orEmpty() else binding.etRemarks.text.toString()
binding.etRemarks.text.let {
config.remarks = if (it.isNullOrEmpty()) profileItem?.remarks.orEmpty() else it.toString()
}
config.server = profileItem?.server
config.serverPort = profileItem?.serverPort
MmkvManager.encodeServerConfig(editGuid, config)
MmkvManager.encodeServerRaw(editGuid, binding.editor.text.toString())
@@ -17,6 +17,7 @@ import android.util.Log
import android.util.Patterns
import android.webkit.URLUtil
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.content.ContextCompat
import com.v2ray.ang.AppConfig
import com.v2ray.ang.AppConfig.ANG_PACKAGE
import com.v2ray.ang.AppConfig.LOOPBACK
@@ -482,5 +483,11 @@ object Utils {
return false
}
fun receiverFlags(): Int = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
ContextCompat.RECEIVER_EXPORTED
} else {
ContextCompat.RECEIVER_NOT_EXPORTED
}
}
@@ -31,7 +31,7 @@ object ZipUtil {
}
}
}
if (filesToCompress.isEmpty) {
if (filesToCompress.isEmpty()) {
return false
}
@@ -8,6 +8,7 @@ import android.content.IntentFilter
import android.content.res.AssetManager
import android.os.Build
import android.util.Log
import androidx.core.content.ContextCompat
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
@@ -25,6 +26,7 @@ import com.v2ray.ang.handler.MmkvManager
import com.v2ray.ang.handler.SettingsManager
import com.v2ray.ang.util.MessageUtil
import com.v2ray.ang.util.SpeedtestUtil
import com.v2ray.ang.util.Utils
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@@ -44,20 +46,15 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
val updateTestResultAction by lazy { MutableLiveData<String>() }
private val tcpingTestScope by lazy { CoroutineScope(Dispatchers.IO) }
/**
* Refer to the official documentation for [registerReceiver](https://developer.android.com/reference/androidx/core/content/ContextCompat#registerReceiver(android.content.Context,android.content.BroadcastReceiver,android.content.IntentFilter,int):
* `registerReceiver(Context, BroadcastReceiver, IntentFilter, int)`.
*/
fun startListenBroadcast() {
isRunning.value = false
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
getApplication<AngApplication>().registerReceiver(
mMsgReceiver,
IntentFilter(AppConfig.BROADCAST_ACTION_ACTIVITY),
Context.RECEIVER_EXPORTED
)
} else {
getApplication<AngApplication>().registerReceiver(
mMsgReceiver,
IntentFilter(AppConfig.BROADCAST_ACTION_ACTIVITY)
)
}
val mFilter = IntentFilter(AppConfig.BROADCAST_ACTION_ACTIVITY)
ContextCompat.registerReceiver(getApplication(), mMsgReceiver, mFilter, Utils.receiverFlags())
MessageUtil.sendMsg2Service(getApplication(), AppConfig.MSG_REGISTER_CLIENT, "")
}
@@ -12,6 +12,7 @@
<!-- Notifications -->
<string name="notification_action_stop_v2ray">إيقاف</string>
<string name="toast_permission_denied">تعذر الحصول على الإذن</string>
<string name="toast_permission_denied_notification">Unable to obtain the notification permission</string>
<string name="notification_action_more">انقر للمزيد</string>
<string name="toast_services_start">بدء الخدمات</string>
<string name="toast_services_stop">إيقاف الخدمات</string>
@@ -12,6 +12,7 @@
<!-- Notifications -->
<string name="notification_action_stop_v2ray">বন্ধ করুন</string>
<string name="toast_permission_denied">অনুমতি পাওয়া যাচ্ছে না</string>
<string name="toast_permission_denied_notification">Unable to obtain the notification permission</string>
<string name="notification_action_more">আরও দেখতে ক্লিক করুন</string>
<string name="toast_services_start">সার্ভিস শুরু করুন</string>
<string name="toast_services_stop">সার্ভিস বন্ধ করুন</string>
+10 -9
View File
@@ -10,8 +10,9 @@
<!-- Notifications -->
<string name="notification_action_stop_v2ray">توقف</string>
<string name="toast_permission_denied">قادر به دریافت مجوز نیست</string>
<string name="notification_action_more">برای اطلاعات بیشتر کلیک کنید</string>
<string name="toast_permission_denied">دریافت مجوز امکان پذیر نیست</string>
<string name="toast_permission_denied_notification">دریافت مجوز اعلان امکان پذیر نیست</string>
<string name="notification_action_more">برای کسب اطلاعات بیشتر کلیک کنید</string>
<string name="toast_services_start">شروع خدمات</string>
<string name="toast_services_stop">توقف خدمات</string>
<string name="toast_services_success">شروع خدمات با موفقیت انجام شد</string>
@@ -159,7 +160,7 @@
<string name="summary_pref_local_dns_enabled">درخواست های DNS به هسته وارد شده و توسط ماژول DNS پردازش می شوند (توصیه می شود در صورت نیاز به مسیریابی برای دور زدن آدرس های LAN و سرزمین اصلی فعال شود)</string>
<string name="title_pref_fake_dns_enabled">فعال کردن DNS جعلی</string>
<string name="summary_pref_fake_dns_enabled">DNS محلی آدرس های آیپی فیک را بر می گرداند (سریع تر می باشد اما ممکن است برای برخی از برنامه ها کار نکند)</string>
<string name="summary_pref_fake_dns_enabled">دی ان اس محلی آدرس های آیپی جعلی را بر می گرداند (سریع تر می باشد و تاخیر را کاهش می دهد اما ممکن است برای برخی از برنامه ها کار نکند)</string>
<string name="title_pref_prefer_ipv6">ترجیح دادن IPv6</string>
<string name="summary_pref_prefer_ipv6">ترجیح دادن نشانی و مسیر های IPv6</string>
@@ -172,7 +173,7 @@
<string name="title_pref_domestic_dns">DNS داخلی (اختیاری)</string>
<string name="summary_pref_domestic_dns">DNS</string>
<string name="title_pref_delay_test_url">True delay test url (http/https)</string>
<string name="title_pref_delay_test_url">آدرس اینترنتی آزمایش تاخیر واقعی کانفیگ ها (http/https)</string>
<string name="summary_pref_delay_test_url">Url</string>
<string name="title_pref_proxy_sharing_enabled">اجازه اتصالات از طریق LAN</string>
@@ -276,11 +277,11 @@
<string name="import_subscription_success">اشتراک با موفقیت ذخیره شد</string>
<string name="import_subscription_failure">ذخیره اشتراک ناموفق بود</string>
<string name="title_fragment_settings">تنظیمات Fragment</string>
<string name="title_pref_fragment_packets">Fragment Packets</string>
<string name="title_pref_fragment_length">Fragment Length (min-max)</string>
<string name="title_pref_fragment_interval">Fragment Interval (min-max)</string>
<string name="title_pref_fragment_enabled">فعال کردن Fragment</string>
<string name="title_fragment_settings">تنظیمات فرگمنت</string>
<string name="title_pref_fragment_packets">بسته های فرگمنت</string>
<string name="title_pref_fragment_length">طول بسته های فرگمنت (حداقل-حداکثر)</string>
<string name="title_pref_fragment_interval">فاصله بین بسته های فرگمنت (حداقل-حداکثر)</string>
<string name="title_pref_fragment_enabled">فعال کردن فرگمنت</string>
<string-array name="share_method">
<item>QRcode</item>
<item>خروجی گرفتن در کلیپ‌ بورد</item>
@@ -11,6 +11,7 @@
<!-- Notifications -->
<string name="notification_action_stop_v2ray">Остановить</string>
<string name="toast_permission_denied">Разрешение не получено</string>
<string name="toast_permission_denied_notification">Разрешение на отображение уведомлений не получено</string>
<string name="notification_action_more">Ещё…</string>
<string name="toast_services_start">Запуск служб</string>
<string name="toast_services_stop">Остановка служб</string>
@@ -11,6 +11,7 @@
<!-- Notifications -->
<string name="notification_action_stop_v2ray">Ngắt kết nối v2rayNG</string>
<string name="toast_permission_denied">Vui lòng cấp quyền cần thiết cho v2rayNG! Bạn đã từ chối các quyền cần thiết như Camera hay Bộ nhớ?</string>
<string name="toast_permission_denied_notification">Unable to obtain the notification permission</string>
<string name="notification_action_more">Nhấn để biết thêm...</string>
<string name="toast_services_start">Đang khởi động v2rayNG...</string>
<string name="toast_services_stop">Đã dừng v2rayNG!</string>
@@ -11,6 +11,7 @@
<!-- Notifications -->
<string name="notification_action_stop_v2ray">停止</string>
<string name="toast_permission_denied">无法取得权限</string>
<string name="toast_permission_denied_notification">无法取得通知权限</string>
<string name="notification_action_more">点击了解更多</string>
<string name="toast_services_start">启动服务中</string>
<string name="toast_services_stop">关闭中</string>
@@ -11,6 +11,7 @@
<!-- Notifications -->
<string name="notification_action_stop_v2ray">停止</string>
<string name="toast_permission_denied">無法取得此權限</string>
<string name="toast_permission_denied_notification">無法取得此通知權限</string>
<string name="notification_action_more">瞭解更多</string>
<string name="toast_services_start">啟動服務</string>
<string name="toast_services_stop">停止服務</string>
@@ -12,6 +12,7 @@
<!-- Notifications -->
<string name="notification_action_stop_v2ray">Stop</string>
<string name="toast_permission_denied">Unable to obtain the permission</string>
<string name="toast_permission_denied_notification">Unable to obtain the notification permission</string>
<string name="notification_action_more">click for more</string>
<string name="toast_services_start">Start Services</string>
<string name="toast_services_stop">Stop Services</string>
+3 -3
View File
@@ -2,7 +2,7 @@
activityKtx = "1.9.3"
appcompat = "1.7.0"
cardview = "1.0.0"
constraintlayout = "2.1.4"
constraintlayout = "2.2.0"
core = "3.5.3"
editorkit = "2.9.0"
flexbox = "3.0.0"
@@ -12,7 +12,7 @@ junit = "4.13.2"
kotlinReflect = "2.0.21"
kotlinxCoroutinesCore = "1.9.0"
legacySupportV4 = "1.0.0"
lifecycleViewmodelKtx = "2.8.6"
lifecycleViewmodelKtx = "2.8.7"
material = "1.12.0"
mmkvStatic = "1.3.9"
multidex = "2.0.1"
@@ -25,7 +25,7 @@ rxpermissions = "0.12"
toastcompat = "1.1.0"
viewpager2 = "1.1.0"
workRuntimeKtx = "2.9.1"
androidGradlePlugin = "8.7.1"
androidGradlePlugin = "8.7.2"
androidKotlinPlugin = "2.0.21"
mockitoMockitoInline = "4.0.0"
+1 -1
View File
@@ -1,6 +1,6 @@
#Sun Jul 28 13:40:50 CST 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
distributionUrl=https://services.gradle.org/distributions/gradle-8.10.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists