Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
413f4efd69 | ||
|
|
de69605eff | ||
|
|
9338ba3525 | ||
|
|
322b6ec615 | ||
|
|
304232d029 | ||
|
|
f80c3bfe07 | ||
|
|
bb0a62fc8b | ||
|
|
5bfdca6cd9 | ||
|
|
447e712a9d | ||
|
|
8bb03f189d | ||
|
|
3b0554cd9b | ||
|
|
858101b0d9 | ||
|
|
a7cf8bee28 | ||
|
|
af1ec7bea9 | ||
|
|
002bf7ef22 | ||
|
|
6919e2336d | ||
|
|
5a5bd22073 | ||
|
|
a726f00f35 | ||
|
|
79297f8a42 | ||
|
|
e3f70ac253 | ||
|
|
838b346fcc | ||
|
|
eba9545ccf |
@@ -11,8 +11,8 @@ android {
|
||||
applicationId = "com.v2ray.ang"
|
||||
minSdk = 21
|
||||
targetSdk = 34
|
||||
versionCode = 565
|
||||
versionName = "1.8.26"
|
||||
versionCode = 579
|
||||
versionName = "1.8.35"
|
||||
multiDexEnabled = true
|
||||
splits.abi {
|
||||
reset()
|
||||
@@ -53,14 +53,14 @@ android {
|
||||
splits {
|
||||
abi {
|
||||
isEnable = true
|
||||
isUniversalApk = false
|
||||
isUniversalApk = true
|
||||
}
|
||||
}
|
||||
|
||||
applicationVariants.all {
|
||||
val variant = this
|
||||
val versionCodes =
|
||||
mapOf("armeabi-v7a" to 1, "arm64-v8a" to 2, "x86" to 3, "x86_64" to 4)
|
||||
mapOf("armeabi-v7a" to 4, "arm64-v8a" to 4, "x86" to 4, "x86_64" to 4, "universal" to 4)
|
||||
|
||||
variant.outputs
|
||||
.map { it as com.android.build.gradle.internal.api.ApkVariantOutputImpl }
|
||||
@@ -68,7 +68,7 @@ android {
|
||||
val abi = if (output.getFilter("ABI") != null)
|
||||
output.getFilter("ABI")
|
||||
else
|
||||
"all"
|
||||
"universal"
|
||||
|
||||
output.outputFileName = "v2rayNG_${variant.versionName}_${abi}.apk"
|
||||
if(versionCodes.containsKey(abi))
|
||||
@@ -86,47 +86,54 @@ android {
|
||||
viewBinding = true
|
||||
buildConfig = true
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
jniLibs {
|
||||
useLegacyPackaging = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.aar","*.jar"))))
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
testImplementation(libs.junit)
|
||||
|
||||
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.7.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.1")
|
||||
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.8.1")
|
||||
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.1")
|
||||
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)
|
||||
}
|
||||
@@ -54,7 +54,7 @@
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
<!-- <category android:name="android.intent.category.LEANBACK_LAUNCHER"/>-->
|
||||
<category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -97,7 +97,13 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
|
||||
holder.itemMainBinding.tvType.text = config.configType.name.lowercase()
|
||||
}
|
||||
}
|
||||
val strState = "${outbound?.getServerAddress()?.dropLast(3)}*** : ${outbound?.getServerPort()}"
|
||||
|
||||
val strState = try{
|
||||
"${outbound?.getServerAddress()?.dropLast(3)}*** : ${outbound?.getServerPort()}"
|
||||
}catch(e: Exception){
|
||||
""
|
||||
}
|
||||
|
||||
holder.itemMainBinding.tvStatistics.text = strState
|
||||
|
||||
holder.itemMainBinding.layoutShare.setOnClickListener {
|
||||
|
||||
@@ -387,22 +387,26 @@ object AngConfigManager {
|
||||
// }
|
||||
|
||||
fun importBatchConfig(server: String?, subid: String, append: Boolean): Int {
|
||||
var count = parseBatchConfig(server, subid, append)
|
||||
var count = parseBatchConfig(Utils.decode(server), subid, append)
|
||||
if (count <= 0) {
|
||||
count = parseBatchConfig(Utils.decode(server), subid, append)
|
||||
count = parseBatchConfig(server, subid, append)
|
||||
}
|
||||
if (count <= 0) {
|
||||
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()
|
||||
""
|
||||
@@ -577,9 +580,9 @@ object AngConfigManager {
|
||||
}
|
||||
|
||||
private fun parseConfigViaSub(server: String?, subid: String, append: Boolean): Int {
|
||||
var count = parseBatchConfig(server, subid, append)
|
||||
var count = parseBatchConfig(Utils.decode(server), subid, append)
|
||||
if (count <= 0) {
|
||||
count = parseBatchConfig(Utils.decode(server), subid, append)
|
||||
count = parseBatchConfig(server, subid, append)
|
||||
}
|
||||
if (count <= 0) {
|
||||
count = parseCustomConfigServer(server, subid)
|
||||
|
||||
@@ -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) {
|
||||
@@ -356,7 +362,7 @@ object Utils {
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun getUrlContentWithCustomUserAgent(urlStr: String?, httpPort: Int = 0): String {
|
||||
fun getUrlContentWithCustomUserAgent(urlStr: String?, timeout: Int = 30000, httpPort: Int = 0): String {
|
||||
val url = URL(urlStr)
|
||||
val conn = if (httpPort == 0) {
|
||||
url.openConnection()
|
||||
@@ -368,6 +374,8 @@ object Utils {
|
||||
)
|
||||
)
|
||||
}
|
||||
conn.connectTimeout = timeout
|
||||
conn.readTimeout = timeout
|
||||
conn.setRequestProperty("Connection", "close")
|
||||
conn.setRequestProperty("User-agent", "v2rayNG/${BuildConfig.VERSION_NAME}")
|
||||
url.userInfo?.let {
|
||||
|
||||
@@ -2,9 +2,11 @@ package com.v2ray.ang.util
|
||||
|
||||
import android.content.Context
|
||||
import android.text.TextUtils
|
||||
import android.util.Log
|
||||
import com.google.gson.Gson
|
||||
import com.tencent.mmkv.MMKV
|
||||
import com.v2ray.ang.AppConfig
|
||||
import com.v2ray.ang.AppConfig.ANG_PACKAGE
|
||||
import com.v2ray.ang.AppConfig.PROTOCOL_FREEDOM
|
||||
import com.v2ray.ang.AppConfig.TAG_DIRECT
|
||||
import com.v2ray.ang.AppConfig.TAG_FRAGMENT
|
||||
@@ -49,6 +51,14 @@ object V2rayConfigUtil {
|
||||
return Result(true, customConfig)
|
||||
}
|
||||
val outbound = config.getProxyOutbound() ?: return Result(false, "")
|
||||
val address = outbound.getServerAddress() ?: 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)
|
||||
//Log.d(ANG_PACKAGE, result.content)
|
||||
return result
|
||||
@@ -134,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()
|
||||
}
|
||||
@@ -162,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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,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)
|
||||
@@ -202,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) {
|
||||
@@ -457,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
|
||||
@@ -589,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
|
||||
}
|
||||
}
|
||||
@@ -10,11 +10,11 @@ import java.net.URI
|
||||
|
||||
object WireguardFmt {
|
||||
fun parseWireguard(str: String): ServerConfig? {
|
||||
val config = ServerConfig.create(EConfigType.WIREGUARD)
|
||||
val uri = URI(Utils.fixIllegalUrl(str))
|
||||
config.remarks = Utils.urlDecode(uri.fragment ?: "")
|
||||
|
||||
if (uri.rawQuery != null) {
|
||||
val config = ServerConfig.create(EConfigType.WIREGUARD)
|
||||
config.remarks = Utils.urlDecode(uri.fragment ?: "")
|
||||
|
||||
val queryParam = uri.rawQuery.split("&")
|
||||
.associate { it.split("=").let { (k, v) -> k to Utils.urlDecode(v) } }
|
||||
|
||||
@@ -25,15 +25,17 @@ object WireguardFmt {
|
||||
?: AppConfig.WIREGUARD_LOCAL_ADDRESS_V4).removeWhiteSpace()
|
||||
.split(",")
|
||||
wireguard.peers?.get(0)?.publicKey = queryParam["publickey"] ?: ""
|
||||
wireguard.peers?.get(0)?.endpoint = "${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 {
|
||||
return null
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
fun toUri(config: ServerConfig): String {
|
||||
|
||||
@@ -46,22 +46,22 @@
|
||||
<string name="server_lab_more_function">Другие параметры</string>
|
||||
<string name="server_lab_head_type">Тип заголовка</string>
|
||||
<string name="server_lab_mode_type">Режим gRPC</string>
|
||||
<string name="server_lab_request_host">host</string>
|
||||
<string name="server_lab_request_host_http">http host</string>
|
||||
<string name="server_lab_request_host_ws">ws host</string>
|
||||
<string name="server_lab_request_host_httpupgrade">httpupgrade host</string>
|
||||
<string name="server_lab_request_host_splithttp">splithttp host</string>
|
||||
<string name="server_lab_request_host_h2">h2 host</string>
|
||||
<string name="server_lab_request_host_quic">QUIC security</string>
|
||||
<string name="server_lab_request_host_grpc">gRPC Authority</string>
|
||||
<string name="server_lab_path">path</string>
|
||||
<string name="server_lab_path_ws">ws path</string>
|
||||
<string name="server_lab_path_httpupgrade">httpupgrade path</string>
|
||||
<string name="server_lab_path_splithttp">splithttp path</string>
|
||||
<string name="server_lab_path_h2">h2 path</string>
|
||||
<string name="server_lab_path_quic">QUIC key</string>
|
||||
<string name="server_lab_path_kcp">kcp seed</string>
|
||||
<string name="server_lab_path_grpc">gRPC serviceName</string>
|
||||
<string name="server_lab_request_host">Узел</string>
|
||||
<string name="server_lab_request_host_http">Узел HTTP</string>
|
||||
<string name="server_lab_request_host_ws">Узел WS</string>
|
||||
<string name="server_lab_request_host_httpupgrade">Узел HTTPUpgrade</string>
|
||||
<string name="server_lab_request_host_splithttp">Узел SplitHTTP</string>
|
||||
<string name="server_lab_request_host_h2">Узел H2</string>
|
||||
<string name="server_lab_request_host_quic">Шифрование QUIC</string>
|
||||
<string name="server_lab_request_host_grpc">Полномочия gRPC</string>
|
||||
<string name="server_lab_path">Путь</string>
|
||||
<string name="server_lab_path_ws">Путь WS</string>
|
||||
<string name="server_lab_path_httpupgrade">Путь HTTPUpgrade</string>
|
||||
<string name="server_lab_path_splithttp">Путь SplitHTTP</string>
|
||||
<string name="server_lab_path_h2">Путь H2</string>
|
||||
<string name="server_lab_path_quic">Ключ QUIC</string>
|
||||
<string name="server_lab_path_kcp">Сид KCP</string>
|
||||
<string name="server_lab_path_grpc">Служба gRPC</string>
|
||||
<string name="server_lab_stream_security">TLS</string>
|
||||
<string name="server_lab_allow_insecure">Разрешать небезопасные</string>
|
||||
<string name="server_lab_sni">SNI</string>
|
||||
@@ -101,6 +101,7 @@
|
||||
<string name="msg_remark_is_duplicate">Описание уже существует</string>
|
||||
<string name="toast_action_not_allowed">Это действие запрещено</string>
|
||||
|
||||
|
||||
<!-- PerAppProxyActivity -->
|
||||
<string name="msg_dialog_progress">Загрузка…</string>
|
||||
<string name="menu_item_search">Поиск</string>
|
||||
@@ -140,7 +141,6 @@
|
||||
<string name="title_pref_route_only_enabled">Домен только для маршрутизации</string>
|
||||
<string name="summary_pref_route_only_enabled">Использовать определённое доменное имя только для маршрутизации и сохранять целевой адрес в виде IP.</string>
|
||||
|
||||
|
||||
<string name="title_pref_local_dns_enabled">Использовать локальную DNS</string>
|
||||
<string name="summary_pref_local_dns_enabled">Обслуживание выполняется DNS-модулем ядра (в настройках маршрутизации рекомендуется выбрать режим «Все, кроме LAN и Китая»)</string>
|
||||
|
||||
@@ -192,7 +192,7 @@
|
||||
<string name="summary_pref_feedback">Предложить улучшение или сообщить об ошибке на GitHub</string>
|
||||
<string name="summary_pref_tg_group">Присоединиться к группе в Telegram</string>
|
||||
<string name="toast_tg_app_not_found">Приложение Telegram не найдено</string>
|
||||
<string name="title_privacy_policy">Конфиденциальность</string>
|
||||
<string name="title_privacy_policy">Политика конфиденциальности</string>
|
||||
<string name="title_about">О приложении</string>
|
||||
<string name="title_source_code">Исходный код</string>
|
||||
<string name="title_tg_channel">Telegram-канал</string>
|
||||
|
||||
@@ -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
V2rayNG/gradle/libs.versions.toml
Normal file
63
V2rayNG/gradle/libs.versions.toml
Normal 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]
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user