Compare commits

...

14 Commits

Author SHA1 Message Date
2dust 413f4efd69 up 1.8.35 2024-07-29 19:12:58 +08:00
Tamim Hossain de69605eff Introduced version catalog for streamlined dependency management (#3377)
This pull request integrates a version catalog (libs.versions.toml) to centralize dependency management within the project. By utilizing this approach, we enhance dependency consistency, reduce maintenance overhead, and facilitate version updates across multiple modules. This change provides a more organized and efficient way to manage project dependencies.
2024-07-28 09:27:58 +08:00
mayampi01 9338ba3525 Set UseIP in Direct to break the Android Private DNS dead loop (#3362)
* Set UseIP in Direct to break the Android Private DNS dead loop

* fakedns: No need to set UseIP again
2024-07-25 19:50:23 +08:00
2dust 322b6ec615 Bug fix 2024-07-25 19:46:39 +08:00
2dust 304232d029 up 1.8.34 2024-07-22 11:34:44 +08:00
2dust f80c3bfe07 up 1.8.33 2024-07-20 15:05:45 +08:00
2dust bb0a62fc8b up 1.8.32 2024-07-18 10:45:05 +08:00
2dust 5bfdca6cd9 Bug fix 2024-07-18 10:15:58 +08:00
2dust 447e712a9d up 1.8.31 2024-07-16 14:05:34 +08:00
2dust 8bb03f189d up 1.8.30 2024-07-13 10:43:46 +08:00
2dust 3b0554cd9b up 1.8.29 2024-07-12 13:30:38 +08:00
2dust 858101b0d9 Bug fix
https://github.com/2dust/v2rayNG/issues/3234
https://github.com/2dust/v2rayNG/issues/3291
2024-07-11 16:41:38 +08:00
2dust a7cf8bee28 Code clean
Add allowInsecure to Share
2024-07-11 16:40:25 +08:00
2dust af1ec7bea9 Bug fix 2024-07-09 12:29:04 +08:00
18 changed files with 297 additions and 231 deletions
+34 -34
View File
@@ -11,8 +11,8 @@ android {
applicationId = "com.v2ray.ang"
minSdk = 21
targetSdk = 34
versionCode = 571
versionName = "1.8.28"
versionCode = 579
versionName = "1.8.35"
multiDexEnabled = true
splits.abi {
reset()
@@ -96,44 +96,44 @@ android {
dependencies {
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.aar","*.jar"))))
testImplementation("junit:junit:4.13.2")
testImplementation(libs.junit)
implementation("com.google.android.flexbox:flexbox:3.0.0")
implementation(libs.flexbox)
// Androidx
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.legacy:legacy-support-v4:1.0.0")
implementation("androidx.appcompat:appcompat:1.7.0")
implementation("com.google.android.material:material:1.12.0")
implementation("androidx.cardview:cardview:1.0.0")
implementation("androidx.preference:preference-ktx:1.2.1")
implementation("androidx.recyclerview:recyclerview:1.3.2")
implementation("androidx.fragment:fragment-ktx:1.8.1")
implementation("androidx.multidex:multidex:2.0.1")
implementation("androidx.viewpager2:viewpager2:1.1.0")
implementation(libs.constraintlayout)
implementation(libs.legacy.support.v4)
implementation(libs.appcompat)
implementation(libs.material)
implementation(libs.cardview)
implementation(libs.preference.ktx)
implementation(libs.recyclerview)
implementation(libs.fragment.ktx)
implementation(libs.multidex)
implementation(libs.viewpager2)
// Androidx ktx
implementation("androidx.activity:activity-ktx:1.9.0")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.8.3")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.3")
implementation(libs.activity.ktx)
implementation(libs.lifecycle.viewmodel.ktx)
implementation(libs.lifecycle.livedata.ktx)
implementation(libs.lifecycle.runtime.ktx)
//kotlin
implementation("org.jetbrains.kotlin:kotlin-reflect:1.9.23")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1")
implementation(libs.kotlin.reflect)
implementation(libs.kotlinx.coroutines.core)
implementation(libs.kotlinx.coroutines.android)
implementation("com.tencent:mmkv-static:1.3.4")
implementation("com.google.code.gson:gson:2.11.0")
implementation("io.reactivex:rxjava:1.3.8")
implementation("io.reactivex:rxandroid:1.2.1")
implementation("com.tbruyelle.rxpermissions:rxpermissions:0.9.4@aar")
implementation("me.drakeet.support:toastcompat:1.1.0")
implementation("com.blacksquircle.ui:editorkit:2.9.0")
implementation("com.blacksquircle.ui:language-base:2.9.0")
implementation("com.blacksquircle.ui:language-json:2.9.0")
implementation("io.github.g00fy2.quickie:quickie-bundled:1.9.0")
implementation("com.google.zxing:core:3.5.3")
implementation(libs.mmkv.static)
implementation(libs.gson)
implementation(libs.rxjava)
implementation(libs.rxandroid)
implementation(libs.rxpermissions)
implementation(libs.toastcompat)
implementation(libs.editorkit)
implementation(libs.language.base)
implementation(libs.language.json)
implementation(libs.quickie.bundled)
implementation(libs.core)
implementation("androidx.work:work-runtime-ktx:2.8.1")
implementation("androidx.work:work-multiprocess:2.8.1")
implementation(libs.work.runtime.ktx)
implementation(libs.work.multiprocess)
}
@@ -81,7 +81,9 @@
},
{
"protocol": "freedom",
"settings": {},
"settings": {
"domainStrategy": "UseIP"
},
"tag": "direct"
},
{
@@ -73,7 +73,7 @@ object V2RayServiceManager {
} else {
context.toast(R.string.toast_services_start)
}
val intent = if (settingsStorage?.decodeString(AppConfig.PREF_MODE) ?: "VPN" == "VPN") {
val intent = if ((settingsStorage?.decodeString(AppConfig.PREF_MODE) ?: "VPN") == "VPN") {
Intent(context.applicationContext, V2RayVpnService::class.java)
} else {
Intent(context.applicationContext, V2RayProxyOnlyService::class.java)
@@ -395,14 +395,18 @@ object AngConfigManager {
count = parseCustomConfigServer(server, subid)
}
if (parseBatchSubscription(server, subid) > 0) {
updateConfigViaSubAll()
return 1
var countSub = parseBatchSubscription(server)
if (countSub <= 0) {
countSub = parseBatchSubscription(Utils.decode(server))
}
return count
if (countSub > 0) {
updateConfigViaSubAll()
}
return count + countSub
}
fun parseBatchSubscription(servers: String?, subid: String): Int {
fun parseBatchSubscription(servers: String?): Int {
try {
if (servers == null) {
return 0
@@ -410,7 +414,6 @@ object AngConfigManager {
var count = 0
servers.lines()
.reversed()
.forEach { str ->
if (str.startsWith(AppConfig.PROTOCOL_HTTP) || str.startsWith(AppConfig.PROTOCOL_HTTPS)) {
count += MmkvManager.importUrlAsSubscription(str)
@@ -488,7 +491,7 @@ object AngConfigManager {
if (serverList.isNotEmpty()) {
var count = 0
for (srv in serverList) {
for (srv in serverList.reversed()) {
val config = ServerConfig.create(EConfigType.CUSTOM)
config.fullConfig =
Gson().fromJson(Gson().toJson(srv), V2rayConfig::class.java)
@@ -560,7 +563,7 @@ object AngConfigManager {
settingsStorage?.decodeString(AppConfig.PREF_HTTP_PORT),
AppConfig.PORT_HTTP.toInt()
)
Utils.getUrlContentWithCustomUserAgent(url, httpPort)
Utils.getUrlContentWithCustomUserAgent(url, 30000, httpPort)
} catch (e: Exception) {
e.printStackTrace()
""
@@ -7,7 +7,6 @@ import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import com.v2ray.ang.dto.AppInfo
import rx.Observable
import java.util.*
object AppManagerUtil {
fun loadNetworkAppList(ctx: Context): ArrayList<AppInfo> {
@@ -31,9 +30,10 @@ object AppManagerUtil {
return apps
}
fun rxLoadNetworkAppList(ctx: Context): Observable<ArrayList<AppInfo>> = Observable.unsafeCreate {
it.onNext(loadNetworkAppList(ctx))
}
fun rxLoadNetworkAppList(ctx: Context): Observable<ArrayList<AppInfo>> =
Observable.unsafeCreate {
it.onNext(loadNetworkAppList(ctx))
}
val PackageInfo.hasInternetPermission: Boolean
get() {
@@ -179,7 +179,7 @@ object MmkvManager {
}
}
fun sortByTestResults( ) {
fun sortByTestResults() {
data class ServerDelay(var guid: String, var testDelayMillis: Long)
val serverDelays = mutableListOf<ServerDelay>()
@@ -7,7 +7,7 @@ import android.content.res.Resources
import android.os.Build
import android.os.LocaleList
import androidx.annotation.RequiresApi
import java.util.*
import java.util.Locale
open class MyContextWrapper(base: Context?) : ContextWrapper(base) {
companion object {
@@ -52,7 +52,8 @@ object SpeedtestUtil {
val allText = process.inputStream.bufferedReader().use { it.readText() }
if (!TextUtils.isEmpty(allText)) {
val tempInfo = allText.substring(allText.indexOf("min/avg/max/mdev") + 19)
val temps = tempInfo.split("/".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
val temps =
tempInfo.split("/".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
if (temps.count() > 0 && temps[0].length < 10) {
return temps[0].toFloat().toInt().toString() + "ms"
}
@@ -70,7 +71,7 @@ object SpeedtestUtil {
tcpTestingSockets.add(socket)
}
val start = System.currentTimeMillis()
socket.connect(InetSocketAddress(url, port),3000)
socket.connect(InetSocketAddress(url, port), 3000)
val time = System.currentTimeMillis() - start
synchronized(this) {
tcpTestingSockets.remove(socket)
@@ -105,8 +106,11 @@ object SpeedtestUtil {
val url = URL(Utils.getDelayTestUrl())
conn = url.openConnection(
Proxy(Proxy.Type.HTTP,
InetSocketAddress("127.0.0.1", port))) as HttpURLConnection
Proxy(
Proxy.Type.HTTP,
InetSocketAddress("127.0.0.1", port)
)
) as HttpURLConnection
conn.connectTimeout = 30000
conn.readTimeout = 30000
conn.setRequestProperty("Connection", "close")
@@ -120,11 +124,19 @@ object SpeedtestUtil {
if (code == 204 || code == 200 && conn.responseLength == 0L) {
result = context.getString(R.string.connection_test_available, elapsed)
} else {
throw IOException(context.getString(R.string.connection_test_error_status_code, code))
throw IOException(
context.getString(
R.string.connection_test_error_status_code,
code
)
)
}
} catch (e: IOException) {
// network exception
Log.d(AppConfig.ANG_PACKAGE, "testConnection IOException: " + Log.getStackTraceString(e))
Log.d(
AppConfig.ANG_PACKAGE,
"testConnection IOException: " + Log.getStackTraceString(e)
)
result = context.getString(R.string.connection_test_error, e.message)
} catch (e: Exception) {
// library exception, eg sumsung
@@ -236,7 +236,13 @@ object Utils {
*/
fun isValidUrl(value: String?): Boolean {
try {
if (value != null && Patterns.WEB_URL.matcher(value).matches() || URLUtil.isValidUrl(value)) {
if (value.isNullOrEmpty()) {
return false
}
if (Patterns.WEB_URL.matcher(value).matches()
|| Patterns.DOMAIN_NAME.matcher(value).matches()
|| URLUtil.isValidUrl(value)
) {
return true
}
} catch (e: Exception) {
@@ -52,9 +52,11 @@ object V2rayConfigUtil {
}
val outbound = config.getProxyOutbound() ?: return Result(false, "")
val address = outbound.getServerAddress() ?: return Result(false, "")
if (!Utils.isIpAddress(address) && !Utils.isValidUrl(address)) {
Log.d(ANG_PACKAGE, "$address is an invalid ip or domain")
return Result(false, "")
if (!Utils.isIpAddress(address)) {
if (!Utils.isValidUrl(address)) {
Log.d(ANG_PACKAGE, "$address is an invalid ip or domain")
return Result(false, "")
}
}
val result = getV2rayNonCustomConfig(context, outbound, config.remarks)
@@ -142,7 +144,8 @@ object V2rayConfigUtil {
settingsStorage?.decodeBool(AppConfig.PREF_SNIFFING_ENABLED, true)
?: true
v2rayConfig.inbounds[0].sniffing?.enabled = fakedns || sniffAllTlsAndHttp
v2rayConfig.inbounds[0].sniffing?.routeOnly = settingsStorage?.decodeBool(AppConfig.PREF_ROUTE_ONLY_ENABLED, false)
v2rayConfig.inbounds[0].sniffing?.routeOnly =
settingsStorage?.decodeBool(AppConfig.PREF_ROUTE_ONLY_ENABLED, false)
if (!sniffAllTlsAndHttp) {
v2rayConfig.inbounds[0].sniffing?.destOverride?.clear()
}
@@ -170,10 +173,6 @@ object V2rayConfigUtil {
&& settingsStorage?.decodeBool(AppConfig.PREF_FAKE_DNS_ENABLED) == true
) {
v2rayConfig.fakedns = listOf(V2rayConfig.FakednsBean())
v2rayConfig.outbounds.filter { it.protocol == PROTOCOL_FREEDOM && it.tag == TAG_DIRECT }
.forEach {
it.settings?.domainStrategy = "UseIP"
}
}
}
@@ -188,7 +187,7 @@ object V2rayConfigUtil {
)
routingUserRule(
settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT)
?: "", AppConfig.TAG_DIRECT, v2rayConfig
?: "", TAG_DIRECT, v2rayConfig
)
routingUserRule(
settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_BLOCKED)
@@ -210,37 +209,38 @@ object V2rayConfigUtil {
when (routingMode) {
ERoutingMode.BYPASS_LAN.value -> {
routingGeo("ip", "private", AppConfig.TAG_DIRECT, v2rayConfig)
routingGeo("ip", "private", TAG_DIRECT, v2rayConfig)
}
ERoutingMode.BYPASS_MAINLAND.value -> {
routingGeo("", "cn", AppConfig.TAG_DIRECT, v2rayConfig)
routingGeo("domain", "geolocation-cn", AppConfig.TAG_DIRECT, v2rayConfig)
routingGeo("", "cn", TAG_DIRECT, v2rayConfig)
routingGeo("domain", "geolocation-cn", TAG_DIRECT, v2rayConfig)
v2rayConfig.routing.rules.add(0, googleapisRoute)
}
ERoutingMode.BYPASS_LAN_MAINLAND.value -> {
routingGeo("ip", "private", AppConfig.TAG_DIRECT, v2rayConfig)
routingGeo("", "cn", AppConfig.TAG_DIRECT, v2rayConfig)
routingGeo("domain", "geolocation-cn", AppConfig.TAG_DIRECT, v2rayConfig)
routingGeo("ip", "private", TAG_DIRECT, v2rayConfig)
routingGeo("", "cn", TAG_DIRECT, v2rayConfig)
routingGeo("domain", "geolocation-cn", TAG_DIRECT, v2rayConfig)
v2rayConfig.routing.rules.add(0, googleapisRoute)
}
ERoutingMode.GLOBAL_DIRECT.value -> {
val globalDirect = V2rayConfig.RoutingBean.RulesBean(
outboundTag = AppConfig.TAG_DIRECT,
outboundTag = TAG_DIRECT,
port = "0-65535"
)
v2rayConfig.routing.rules.add(globalDirect)
}
}
if(routingMode != ERoutingMode.GLOBAL_DIRECT.value) {
if (routingMode != ERoutingMode.GLOBAL_DIRECT.value) {
v2rayConfig.routing.rules.add(
V2rayConfig.RoutingBean.RulesBean(
outboundTag = AppConfig.TAG_PROXY,
port = "0-65535"
))
)
)
}
} catch (e: Exception) {
@@ -465,7 +465,7 @@ object V2rayConfigUtil {
if (Utils.isPureIpAddress(domesticDns.first())) {
v2rayConfig.routing.rules.add(
0, V2rayConfig.RoutingBean.RulesBean(
outboundTag = AppConfig.TAG_DIRECT,
outboundTag = TAG_DIRECT,
port = "53",
ip = arrayListOf(domesticDns.first()),
domain = null
@@ -597,7 +597,8 @@ object V2rayConfigUtil {
mux = null
)
var packets = settingsStorage?.decodeString(AppConfig.PREF_FRAGMENT_PACKETS) ?: "tlshello"
var packets =
settingsStorage?.decodeString(AppConfig.PREF_FRAGMENT_PACKETS) ?: "tlshello"
if (v2rayConfig.outbounds[0].streamSettings?.security == V2rayConfig.REALITY
&& packets == "tlshello"
) {
@@ -96,7 +96,7 @@ object ShadowsocksFmt {
for (pair in pairs) {
val idx = pair.indexOf("=")
if (idx == -1) {
queryPairs[Utils.urlDecode(pair)] = "";
queryPairs[Utils.urlDecode(pair)] = ""
} else {
queryPairs[Utils.urlDecode(pair.substring(0, idx))] =
Utils.urlDecode(pair.substring(idx + 1))
@@ -118,9 +118,9 @@ object ShadowsocksFmt {
null
)
} else if (queryPairs["plugin"] == "v2ray-plugin") {
var network = "ws";
var network = "ws"
if (queryPairs["mode"] == "quic") {
network = "quic";
network = "quic"
}
sni = config.outboundBean?.streamSettings?.populateTransportSettings(
network,
@@ -20,7 +20,7 @@ object TrojanFmt {
}
fun parseTrojan(str: String): ServerConfig? {
val allowInsecure = settingsStorage?.decodeBool(AppConfig.PREF_ALLOW_INSECURE) ?: false
var allowInsecure = settingsStorage?.decodeBool(AppConfig.PREF_ALLOW_INSECURE) ?: false
val config = ServerConfig.create(EConfigType.TROJAN)
val uri = URI(Utils.fixIllegalUrl(str))
@@ -30,8 +30,14 @@ object TrojanFmt {
var fingerprint = config.outboundBean?.streamSettings?.tlsSettings?.fingerprint
if (uri.rawQuery.isNullOrEmpty()) {
config.outboundBean?.streamSettings?.populateTlsSettings(
V2rayConfig.TLS, allowInsecure, "",
fingerprint, null, null, null, null
V2rayConfig.TLS,
allowInsecure,
"",
fingerprint,
null,
null,
null,
null
)
} else {
val queryParam = uri.rawQuery.split("&")
@@ -50,14 +56,19 @@ object TrojanFmt {
queryParam["authority"]
)
fingerprint = queryParam["fp"] ?: ""
allowInsecure = if ((queryParam["allowInsecure"] ?: "") == "1") true else allowInsecure
config.outboundBean?.streamSettings?.populateTlsSettings(
queryParam["security"] ?: V2rayConfig.TLS,
allowInsecure, queryParam["sni"] ?: sni?:"", fingerprint, queryParam["alpn"],
null, null, null
allowInsecure,
queryParam["sni"] ?: sni ?: "",
fingerprint,
queryParam["alpn"],
null,
null,
null
)
flow = queryParam["flow"] ?: ""
}
config.outboundBean?.settings?.servers?.get(0)?.let { server ->
server.address = uri.idnHost
server.port = uri.port
@@ -78,7 +89,6 @@ object TrojanFmt {
if (!TextUtils.isEmpty(it)) {
dicQuery["flow"] = it
}
}
dicQuery["security"] = streamSetting.security.ifEmpty { "none" }
@@ -92,16 +102,16 @@ object TrojanFmt {
Utils.removeWhiteSpace(tlsSetting.alpn.joinToString()).orEmpty()
}
if (!TextUtils.isEmpty(tlsSetting.fingerprint)) {
dicQuery["fp"] = tlsSetting.fingerprint?:""
dicQuery["fp"] = tlsSetting.fingerprint ?: ""
}
if (!TextUtils.isEmpty(tlsSetting.publicKey)) {
dicQuery["pbk"] = tlsSetting.publicKey?:""
dicQuery["pbk"] = tlsSetting.publicKey ?: ""
}
if (!TextUtils.isEmpty(tlsSetting.shortId)) {
dicQuery["sid"] = tlsSetting.shortId?:""
dicQuery["sid"] = tlsSetting.shortId ?: ""
}
if (!TextUtils.isEmpty(tlsSetting.spiderX)) {
dicQuery["spx"] = Utils.urlEncode(tlsSetting.spiderX?:"")
dicQuery["spx"] = Utils.urlEncode(tlsSetting.spiderX ?: "")
}
}
dicQuery["type"] =
@@ -20,7 +20,7 @@ object VlessFmt {
}
fun parseVless(str: String): ServerConfig? {
val allowInsecure = settingsStorage?.decodeBool(AppConfig.PREF_ALLOW_INSECURE) ?: false
var allowInsecure = settingsStorage?.decodeBool(AppConfig.PREF_ALLOW_INSECURE) ?: false
val config = ServerConfig.create(EConfigType.VLESS)
val uri = URI(Utils.fixIllegalUrl(str))
@@ -31,7 +31,7 @@ object VlessFmt {
val streamSetting = config.outboundBean?.streamSettings ?: return null
config.remarks = Utils.urlDecode(uri.fragment ?: "")
config.outboundBean?.settings?.vnext?.get(0)?.let { vnext ->
config.outboundBean.settings?.vnext?.get(0)?.let { vnext ->
vnext.address = uri.idnHost
vnext.port = uri.port
vnext.users[0].id = uri.userInfo
@@ -51,6 +51,7 @@ object VlessFmt {
queryParam["serviceName"],
queryParam["authority"]
)
allowInsecure = if ((queryParam["allowInsecure"] ?: "") == "1") true else allowInsecure
streamSetting.populateTlsSettings(
queryParam["security"] ?: "",
allowInsecure,
@@ -92,16 +93,16 @@ object VlessFmt {
Utils.removeWhiteSpace(tlsSetting.alpn.joinToString()).orEmpty()
}
if (!TextUtils.isEmpty(tlsSetting.fingerprint)) {
dicQuery["fp"] = tlsSetting.fingerprint?:""
dicQuery["fp"] = tlsSetting.fingerprint ?: ""
}
if (!TextUtils.isEmpty(tlsSetting.publicKey)) {
dicQuery["pbk"] = tlsSetting.publicKey?:""
dicQuery["pbk"] = tlsSetting.publicKey ?: ""
}
if (!TextUtils.isEmpty(tlsSetting.shortId)) {
dicQuery["sid"] = tlsSetting.shortId?:""
dicQuery["sid"] = tlsSetting.shortId ?: ""
}
if (!TextUtils.isEmpty(tlsSetting.spiderX)) {
dicQuery["spx"] = Utils.urlEncode(tlsSetting.spiderX?:"")
dicQuery["spx"] = Utils.urlEncode(tlsSetting.spiderX ?: "")
}
}
dicQuery["type"] =
@@ -22,68 +22,64 @@ object VmessFmt {
}
fun parseVmess(str: String): ServerConfig? {
if (str.indexOf('?') > 0 && str.indexOf('&') > 0) {
return parseVmessStd(str)
}
val allowInsecure = settingsStorage?.decodeBool(AppConfig.PREF_ALLOW_INSECURE) ?: false
val config = ServerConfig.create(EConfigType.VMESS)
val streamSetting = config.outboundBean?.streamSettings ?: return null
if (!tryParseNewVmess(str, config, allowInsecure)) {
if (str.indexOf("?") > 0) {
if (!tryResolveVmess4Kitsunebi(str, config)) {
Log.d(AppConfig.ANG_PACKAGE, "R.string.toast_incorrect_protocol")
return null
}
} else {
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")
return null
}
val vmessQRCode = Gson().fromJson(result, VmessQRCode::class.java)
// Although VmessQRCode fields are non null, looks like Gson may still create null fields
if (TextUtils.isEmpty(vmessQRCode.add) || TextUtils.isEmpty(vmessQRCode.port) || TextUtils.isEmpty(
vmessQRCode.id
) || TextUtils.isEmpty(vmessQRCode.net)
) {
Log.d(AppConfig.ANG_PACKAGE, "R.string.toast_incorrect_protocol")
return null
}
config.remarks = vmessQRCode.ps
config.outboundBean?.settings?.vnext?.get(0)?.let { vnext ->
vnext.address = vmessQRCode.add
vnext.port = Utils.parseInt(vmessQRCode.port)
vnext.users[0].id = vmessQRCode.id
vnext.users[0].security =
if (TextUtils.isEmpty(vmessQRCode.scy)) V2rayConfig.DEFAULT_SECURITY else vmessQRCode.scy
vnext.users[0].alterId = Utils.parseInt(vmessQRCode.aid)
}
val sni = streamSetting.populateTransportSettings(
vmessQRCode.net,
vmessQRCode.type,
vmessQRCode.host,
vmessQRCode.path,
vmessQRCode.path,
vmessQRCode.host,
vmessQRCode.path,
vmessQRCode.type,
vmessQRCode.path,
vmessQRCode.host
)
val fingerprint = vmessQRCode.fp ?: streamSetting.tlsSettings?.fingerprint
streamSetting.populateTlsSettings(
vmessQRCode.tls,
allowInsecure,
if (TextUtils.isEmpty(vmessQRCode.sni)) sni else vmessQRCode.sni,
fingerprint,
vmessQRCode.alpn,
null,
null,
null
)
}
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")
return null
}
val vmessQRCode = Gson().fromJson(result, VmessQRCode::class.java)
// Although VmessQRCode fields are non null, looks like Gson may still create null fields
if (TextUtils.isEmpty(vmessQRCode.add)
|| TextUtils.isEmpty(vmessQRCode.port)
|| TextUtils.isEmpty(vmessQRCode.id)
|| TextUtils.isEmpty(vmessQRCode.net)
) {
Log.d(AppConfig.ANG_PACKAGE, "R.string.toast_incorrect_protocol")
return null
}
config.remarks = vmessQRCode.ps
config.outboundBean.settings?.vnext?.get(0)?.let { vnext ->
vnext.address = vmessQRCode.add
vnext.port = Utils.parseInt(vmessQRCode.port)
vnext.users[0].id = vmessQRCode.id
vnext.users[0].security =
if (TextUtils.isEmpty(vmessQRCode.scy)) V2rayConfig.DEFAULT_SECURITY else vmessQRCode.scy
vnext.users[0].alterId = Utils.parseInt(vmessQRCode.aid)
}
val sni = streamSetting.populateTransportSettings(
vmessQRCode.net,
vmessQRCode.type,
vmessQRCode.host,
vmessQRCode.path,
vmessQRCode.path,
vmessQRCode.host,
vmessQRCode.path,
vmessQRCode.type,
vmessQRCode.path,
vmessQRCode.host
)
val fingerprint = vmessQRCode.fp
streamSetting.populateTlsSettings(
vmessQRCode.tls,
allowInsecure,
if (TextUtils.isEmpty(vmessQRCode.sni)) sni else vmessQRCode.sni,
fingerprint,
vmessQRCode.alpn,
null,
null,
null
)
return config
}
@@ -114,80 +110,51 @@ object VmessFmt {
return Utils.encode(json)
}
private fun tryParseNewVmess(
uriString: String, config: ServerConfig, allowInsecure: Boolean
): Boolean {
return runCatching {
val uri = URI(Utils.fixIllegalUrl(uriString))
check(uri.scheme == "vmess")
val (_, protocol, tlsStr, uuid, alterId) = Regex("(tcp|http|ws|kcp|quic|grpc)(\\+tls)?:([0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12})").matchEntire(
uri.userInfo
)?.groupValues ?: error("parse user info fail.")
val tls = tlsStr.isNotBlank()
val queryParam = uri.rawQuery.split("&")
.associate { it.split("=").let { (k, v) -> k to Utils.urlDecode(v) } }
fun parseVmessStd(str: String): ServerConfig? {
var allowInsecure = settingsStorage?.decodeBool(AppConfig.PREF_ALLOW_INSECURE) ?: false
val config = ServerConfig.create(EConfigType.VMESS)
val streamSetting = config.outboundBean?.streamSettings ?: return false
config.remarks = Utils.urlDecode(uri.fragment ?: "")
config.outboundBean.settings?.vnext?.get(0)?.let { vnext ->
vnext.address = uri.idnHost
vnext.port = uri.port
vnext.users[0].id = uuid
vnext.users[0].security = V2rayConfig.DEFAULT_SECURITY
vnext.users[0].alterId = alterId.toInt()
}
var fingerprint = streamSetting.tlsSettings?.fingerprint
val sni = streamSetting.populateTransportSettings(protocol,
queryParam["type"],
queryParam["host"]?.split("|")?.get(0) ?: "",
queryParam["path"]?.takeIf { it.trim() != "/" } ?: "",
queryParam["seed"],
queryParam["security"],
queryParam["key"],
queryParam["mode"],
queryParam["serviceName"],
queryParam["authority"])
streamSetting.populateTlsSettings(
if (tls) V2rayConfig.TLS else "",
allowInsecure,
sni,
fingerprint,
null,
null,
null,
null
)
true
}.getOrElse { false }
}
val uri = URI(Utils.fixIllegalUrl(str))
if (uri.rawQuery.isNullOrEmpty()) return null
val queryParam = uri.rawQuery.split("&")
.associate { it.split("=").let { (k, v) -> k to Utils.urlDecode(v) } }
private fun tryResolveVmess4Kitsunebi(server: String, config: ServerConfig): Boolean {
val streamSetting = config.outboundBean?.streamSettings ?: return null
var result = server.replace(EConfigType.VMESS.protocolScheme, "")
val indexSplit = result.indexOf("?")
if (indexSplit > 0) {
result = result.substring(0, indexSplit)
}
result = Utils.decode(result)
val arr1 = result.split('@')
if (arr1.count() != 2) {
return false
}
val arr21 = arr1[0].split(':')
val arr22 = arr1[1].split(':')
if (arr21.count() != 2) {
return false
}
config.remarks = "Alien"
config.outboundBean?.settings?.vnext?.get(0)?.let { vnext ->
vnext.address = arr22[0]
vnext.port = Utils.parseInt(arr22[1])
vnext.users[0].id = arr21[1]
vnext.users[0].security = arr21[0]
config.remarks = Utils.urlDecode(uri.fragment ?: "")
config.outboundBean.settings?.vnext?.get(0)?.let { vnext ->
vnext.address = uri.idnHost
vnext.port = uri.port
vnext.users[0].id = uri.userInfo
vnext.users[0].security = V2rayConfig.DEFAULT_SECURITY
vnext.users[0].alterId = 0
}
return true
val sni = streamSetting.populateTransportSettings(
queryParam["type"] ?: "tcp",
queryParam["headerType"],
queryParam["host"],
queryParam["path"],
queryParam["seed"],
queryParam["quicSecurity"],
queryParam["key"],
queryParam["mode"],
queryParam["serviceName"],
queryParam["authority"]
)
allowInsecure = if ((queryParam["allowInsecure"] ?: "") == "1") true else allowInsecure
streamSetting.populateTlsSettings(
queryParam["security"] ?: "",
allowInsecure,
queryParam["sni"] ?: sni,
queryParam["fp"] ?: "",
queryParam["alpn"],
null,
null,
null
)
return config
}
}
@@ -25,14 +25,15 @@ object WireguardFmt {
?: AppConfig.WIREGUARD_LOCAL_ADDRESS_V4).removeWhiteSpace()
.split(",")
wireguard.peers?.get(0)?.publicKey = queryParam["publickey"] ?: ""
wireguard.peers?.get(0)?.endpoint = Utils.getIpv6Address(uri.idnHost) + ":${uri.port}"
wireguard.peers?.get(0)?.endpoint =
Utils.getIpv6Address(uri.idnHost) + ":${uri.port}"
wireguard.mtu = Utils.parseInt(queryParam["mtu"] ?: AppConfig.WIREGUARD_LOCAL_MTU)
wireguard.reserved =
(queryParam["reserved"] ?: "0,0,0").removeWhiteSpace().split(",")
.map { it.toInt() }
}
return config
}else {
} else {
return null
}
}
+2 -2
View File
@@ -1,6 +1,6 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id("com.android.application") version "8.2.2" apply false
id("com.android.library") version "8.2.2" apply false
id("com.android.application") version "8.4.2" apply false
id("com.android.library") version "8.4.2" apply false
id("org.jetbrains.kotlin.android") version "1.9.23" apply false
}
+63
View File
@@ -0,0 +1,63 @@
[versions]
activityKtx = "1.9.1"
appcompat = "1.7.0"
cardview = "1.0.0"
constraintlayout = "2.1.4"
core = "3.5.3"
editorkit = "2.9.0"
flexbox = "3.0.0"
fragmentKtx = "1.8.2"
gson = "2.11.0"
junit = "4.13.2"
kotlinReflect = "2.0.0"
kotlinxCoroutinesCore = "1.8.1"
legacySupportV4 = "1.0.0"
lifecycleViewmodelKtx = "2.8.4"
material = "1.12.0"
mmkvStatic = "1.3.4"
multidex = "2.0.1"
preferenceKtx = "1.2.1"
quickieBundled = "1.9.0"
recyclerview = "1.3.2"
rxandroid = "1.2.1"
rxjava = "1.3.8"
rxpermissions = "0.9.4"
toastcompat = "1.1.0"
viewpager2 = "1.1.0"
workRuntimeKtx = "2.8.1"
[libraries]
activity-ktx = { module = "androidx.activity:activity-ktx", version.ref = "activityKtx" }
appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" }
cardview = { module = "androidx.cardview:cardview", version.ref = "cardview" }
constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "constraintlayout" }
core = { module = "com.google.zxing:core", version.ref = "core" }
editorkit = { module = "com.blacksquircle.ui:editorkit", version.ref = "editorkit" }
flexbox = { module = "com.google.android.flexbox:flexbox", version.ref = "flexbox" }
fragment-ktx = { module = "androidx.fragment:fragment-ktx", version.ref = "fragmentKtx" }
gson = { module = "com.google.code.gson:gson", version.ref = "gson" }
junit = { module = "junit:junit", version.ref = "junit" }
kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlinReflect" }
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinxCoroutinesCore" }
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutinesCore" }
language-base = { module = "com.blacksquircle.ui:language-base", version.ref = "editorkit" }
language-json = { module = "com.blacksquircle.ui:language-json", version.ref = "editorkit" }
legacy-support-v4 = { module = "androidx.legacy:legacy-support-v4", version.ref = "legacySupportV4" }
lifecycle-livedata-ktx = { module = "androidx.lifecycle:lifecycle-livedata-ktx", version.ref = "lifecycleViewmodelKtx" }
lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycleViewmodelKtx" }
lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "lifecycleViewmodelKtx" }
material = { module = "com.google.android.material:material", version.ref = "material" }
mmkv-static = { module = "com.tencent:mmkv-static", version.ref = "mmkvStatic" }
multidex = { module = "androidx.multidex:multidex", version.ref = "multidex" }
quickie-bundled = { module = "io.github.g00fy2.quickie:quickie-bundled", version.ref = "quickieBundled" }
recyclerview = { module = "androidx.recyclerview:recyclerview", version.ref = "recyclerview" }
preference-ktx = { module = "androidx.preference:preference-ktx", version.ref = "preferenceKtx" }
rxandroid = { module = "io.reactivex:rxandroid", version.ref = "rxandroid" }
rxjava = { module = "io.reactivex:rxjava", version.ref = "rxjava" }
rxpermissions = { module = "com.tbruyelle.rxpermissions:rxpermissions", version.ref = "rxpermissions" }
toastcompat = { module = "me.drakeet.support:toastcompat", version.ref = "toastcompat" }
viewpager2 = { module = "androidx.viewpager2:viewpager2", version.ref = "viewpager2" }
work-multiprocess = { module = "androidx.work:work-multiprocess", version.ref = "workRuntimeKtx" }
work-runtime-ktx = { module = "androidx.work:work-runtime-ktx", version.ref = "workRuntimeKtx" }
[plugins]
+2 -2
View File
@@ -1,6 +1,6 @@
#Sun Jun 21 12:33:19 CST 2020
#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
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip