From c94a5fb743c1001ccab202c39e3aeb311b05659c Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Tue, 7 Jan 2025 14:47:18 +0800
Subject: [PATCH 001/238] Update Luri Bakhtiari translation
---
.../src/main/res/values-bqi-rIR/strings.xml | 52 +++++++++----------
1 file changed, 26 insertions(+), 26 deletions(-)
diff --git a/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml b/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml
index 069d720d..57901b41 100644
--- a/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml
+++ b/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml
@@ -40,16 +40,16 @@
کانفیگ سفارشین ز آدرس اینترنتی و من بیار
نشۊوی اینترنتی اسکن کانفیگ سفارشین بزݩ
پاک بۊ؟
- پؽش ز پاک کردن کانفیگ نا موئتبر قوۊل کوݩ! پاک کردن کانفیگن قوۊل اکۊنی؟
+ پؽش ز پاک کردن کانفیگ نا موئتبر واجۊری کوݩ! پاک کردن کانفیگن قوۊل اکۊنی؟
نیشتنا
آدرس
پورت
نوم من توری
- alterId
+ شناسه جایگۊزین
ٱمنیت
شبکه
جاگورو
- نوء head
+ نوء سر بلگ
هالت gRPC
هاست
هاست http
@@ -58,14 +58,14 @@
هاست xhttp
هاست h2
ٱمنیت QUIC
- اختیار gRPC
+ Authority gRPC
تور
- تور ws
- تور httpupgrade
- تور xhttp
- تور h2
- کیلیت QUIC
- سید kcp
+ تور WS
+ تور HTTPUpgrade
+ تور XHTTP
+ تور H2
+ تور QUIC
+ KCP seed
نوم خدمات gRPC
TLS
Fingerprint
@@ -82,12 +82,12 @@
جریان
کیلیت پوی وولاتی
کیلیت رمز ناهاڌن ازاف (اختیاری)
- ShortId
+ ShortID
SpiderX
کیلیت سیخومی
Reserved(اختیاری، وا کاما ز یک جوڌا ابۊن)
آدرس مهلی (اختیاری IPv4/IPv6، وا کاما ز یک جوڌا ابۊن)
- Mtu(اختیاری، خوتکار 1420)
+ Mtu(اختیاری، پؽش فرز 1420)
وا مووفقیت ٱنجوم وابی
شکست خرد
هیچ داده ای وۊجۊڌ نڌاره
@@ -107,7 +107,7 @@
لف گیری فایل ٱنجوم نوابی، ز ی برنومه دؽوۉداری فایل هیاری بگرین
ازاف کردن دارایی
ازاف کردن فایل
- ازاف کردن آدرس اینترنتی
+ ازاف کردن لینگ
اسکن QRcode
آدرس اینترنتی
دانلود فایلا
@@ -115,7 +115,7 @@
فایلن نجوست
ائزارات ز زیتر بیڌسۉݩ
ای کار ممنۊ هڌ
- رزم Obfs
+ رزم obfs
پورت گوم (درگا سرورن ز نۊ هؽل اکونه)
فاسله پورت گوم (سانیه)
pinSHA256
@@ -176,7 +176,7 @@
DNS
VPN DNS (تینا IPv4/v6)
- Does VPN bypass LAN
+ VPN ز شبکه مهلی اگوڌرته؟
DNS منی (اختیاری)
DNS
@@ -187,9 +187,9 @@
آدرس اینترنتی آزمایش تئخیر واقعی (http/https)
نشۊوی اینترنتی
- هشتن منپیزا ز LAN
+ هشتن منپیزا ز شبکه مهلی
پوی دسگایل ترن وا آدرس IP ایسا، ز ر socks/http و پروکسی منپیز بۊن، تینا من شبکه قابل اعتماد فعال بۊ تا ز منپیز ؛یر موجاز جلو گری بۊ.
- منپیزا ز LAN ن موجار کۊنین، موتمعن بۊین ک من ی شبکه قابل ائتماڌ هڌین.
+ منپیزا ز شبکه مهلی ن موجار کۊنین، موتمعن بۊین ک من ی شبکه قابل ائتماڌ هڌین.
اجازه نا ٱمن
مجال و کار بردن TLS ب تۉر پؽش فرز، موجوز نا ٱمن فعال هڌ.
@@ -232,7 +232,7 @@
سئت داسووا
هالت
- سی هیاری بیشتر ری ای هؽل بزݩ
+ سی دووسمندیا وو هیاری بیشتر، ری ای هؽل بزݩ
زۉݩ
سامووا رابت منتوری
سامووا هالت رابت منتوری
@@ -248,12 +248,12 @@
سامووا جرگه اشتراک
نیشتنا
نشۊوی اینترنتی اختیاری
- نیشتنا فیلتر مئمۊلی
+ نوم موستعار فیلتر
فعال بیڌن ورۊ کردن
فعال بیڌن ورۊ کردن خوتکار
- نیشتنا پروکسی پؽشی
- نیشتنا پروکسی نیایی
- نیشتنا هڌسۉݩ وو هرف نارن
+ نوم موستعار پروکسی دیندایی
+ نوم موستعار پروکسی نیایی
+ موتمعن بۊ ک نوم موستعار هڌس وو جۊرس نی
ورۊ کردن اشتراک جرگه سکویی
Tcping کانفیگا جرگه سکویی
تئخیر واقعی کانفیگا جرگه سکویی
@@ -340,9 +340,9 @@
- - Follow config
- - Bypass
- - Not Bypass
+ - پؽش فرز کانفیگ
+ - دور زیڌه بۊ
+ - دور زیڌه نبۊ
-
+
\ No newline at end of file
From efd07167074f639aec7ca2c7d0478cf6f2d75c67 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Tue, 7 Jan 2025 17:14:23 +0800
Subject: [PATCH 002/238] Custom configuration can use any outbound
https://github.com/2dust/v2rayNG/issues/4243
---
V2rayNG/app/src/main/java/com/v2ray/ang/dto/ProfileItem.kt | 5 +++++
.../main/java/com/v2ray/ang/service/V2RayServiceManager.kt | 6 +++++-
2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/ProfileItem.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/ProfileItem.kt
index 883a3788..1a0c7807 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/ProfileItem.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/ProfileItem.kt
@@ -1,5 +1,7 @@
package com.v2ray.ang.dto
+import com.v2ray.ang.AppConfig.LOOPBACK
+import com.v2ray.ang.AppConfig.PORT_SOCKS
import com.v2ray.ang.AppConfig.TAG_BLOCKED
import com.v2ray.ang.AppConfig.TAG_DIRECT
import com.v2ray.ang.AppConfig.TAG_PROXY
@@ -66,6 +68,9 @@ data class ProfileItem(
}
fun getServerAddressAndPort(): String {
+ if (server.isNullOrEmpty() && configType == EConfigType.CUSTOM) {
+ return "$LOOPBACK:$PORT_SOCKS"
+ }
return Utils.getIpv6Address(server) + ":" + serverPort
}
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt
index a008a840..b94c7a10 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt
@@ -20,6 +20,7 @@ import com.v2ray.ang.AppConfig.ANG_PACKAGE
import com.v2ray.ang.AppConfig.TAG_DIRECT
import com.v2ray.ang.AppConfig.VPN
import com.v2ray.ang.R
+import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.dto.ProfileItem
import com.v2ray.ang.extension.toSpeedString
import com.v2ray.ang.extension.toast
@@ -68,7 +69,10 @@ object V2RayServiceManager {
if (v2rayPoint.isRunning) return
val guid = MmkvManager.getSelectServer() ?: return
val config = MmkvManager.decodeServerConfig(guid) ?: return
- if (!Utils.isValidUrl(config.server) && !Utils.isIpAddress(config.server)) return
+ if (config.configType != EConfigType.CUSTOM
+ && !Utils.isValidUrl(config.server)
+ && !Utils.isIpAddress(config.server)
+ ) return
// val result = V2rayConfigUtil.getV2rayConfig(context, guid)
// if (!result.status) return
From 3a6e23bcefcbd26c9fcf5b256de14d94ba4ad687 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Wed, 8 Jan 2025 11:23:21 +0800
Subject: [PATCH 003/238] Fix the bug of mux parameter taking
---
.../main/java/com/v2ray/ang/dto/V2rayConfig.kt | 6 +++---
.../java/com/v2ray/ang/handler/MmkvManager.kt | 4 ----
.../com/v2ray/ang/handler/V2rayConfigManager.kt | 17 +++++++----------
3 files changed, 10 insertions(+), 17 deletions(-)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt
index 20b8b251..8c3b7761 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt
@@ -476,9 +476,9 @@ data class V2rayConfig(
data class MuxBean(
var enabled: Boolean,
- var concurrency: Int = 8,
- var xudpConcurrency: Int = 8,
- var xudpProxyUDP443: String = "",
+ var concurrency: Int? = null,
+ var xudpConcurrency: Int? = null,
+ var xudpProxyUDP443: String? = null,
)
fun getServerAddress(): String? {
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/MmkvManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/MmkvManager.kt
index e8cc78fe..773699d0 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/MmkvManager.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/MmkvManager.kt
@@ -349,10 +349,6 @@ object MmkvManager {
return settingsStorage.decodeBool(key, defaultValue)
}
- fun decodeSettingsInt(key: String, defaultValue: Int): Int {
- return settingsStorage.decodeInt(key, defaultValue)
- }
-
fun decodeSettingsStringSet(key: String): MutableSet? {
return settingsStorage.decodeStringSet(key)
}
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt
index 8c432dde..72d81345 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt
@@ -438,21 +438,18 @@ object V2rayConfigManager {
|| protocol.equals(EConfigType.HYSTERIA2.name, true)
) {
muxEnabled = false
- } else if (protocol.equals(EConfigType.VLESS.name, true)
- && outbound.settings?.vnext?.first()?.users?.first()?.flow?.isNotEmpty() == true
- ) {
- muxEnabled = false
} else if (outbound.streamSettings?.network == NetworkType.XHTTP.type) {
muxEnabled = false
}
+
if (muxEnabled == true) {
outbound.mux?.enabled = true
- outbound.mux?.concurrency =
- MmkvManager.decodeSettingsInt(AppConfig.PREF_MUX_CONCURRENCY, 8)
- outbound.mux?.xudpConcurrency =
- MmkvManager.decodeSettingsInt(AppConfig.PREF_MUX_XUDP_CONCURRENCY, 16)
- outbound.mux?.xudpProxyUDP443 =
- MmkvManager.decodeSettingsString(AppConfig.PREF_MUX_XUDP_QUIC) ?: "reject"
+ outbound.mux?.concurrency = MmkvManager.decodeSettingsString(AppConfig.PREF_MUX_CONCURRENCY, "8").orEmpty().toInt()
+ outbound.mux?.xudpConcurrency = MmkvManager.decodeSettingsString(AppConfig.PREF_MUX_XUDP_CONCURRENCY, "16").orEmpty().toInt()
+ outbound.mux?.xudpProxyUDP443 = MmkvManager.decodeSettingsString(AppConfig.PREF_MUX_XUDP_QUIC,"reject")
+ if (protocol.equals(EConfigType.VLESS.name, true) && outbound.settings?.vnext?.first()?.users?.first()?.flow?.isNotEmpty() == true) {
+ outbound.mux?.concurrency = -1
+ }
} else {
outbound.mux?.enabled = false
outbound.mux?.concurrency = -1
From 4d875bc3d40a11f1b4a6bf849944a470e382a40e Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Thu, 9 Jan 2025 09:48:37 +0800
Subject: [PATCH 004/238] Add theme to SwitchCompat for tasker
---
V2rayNG/app/src/main/res/layout/activity_tasker.xml | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/V2rayNG/app/src/main/res/layout/activity_tasker.xml b/V2rayNG/app/src/main/res/layout/activity_tasker.xml
index fc3d62e1..6fb211b1 100644
--- a/V2rayNG/app/src/main/res/layout/activity_tasker.xml
+++ b/V2rayNG/app/src/main/res/layout/activity_tasker.xml
@@ -1,4 +1,5 @@
+ android:checked="true"
+ app:theme="@style/BrandedSwitch" />
From e0c8ece9b54beb64910a78c862f0fa72eebba322 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E1=A1=A0=E1=A0=B5=E1=A1=A0=E1=A1=B3=20=E1=A1=A0=E1=A0=B5?=
=?UTF-8?q?=E1=A1=A0=20=E1=A0=AE=E1=A0=A0=E1=A0=A8=E1=A1=A9=E1=A0=8B?=
=?UTF-8?q?=E1=A0=A0=E1=A0=A8?=
<125150101+UjuiUjuMandan@users.noreply.github.com>
Date: Sat, 11 Jan 2025 11:38:47 +0900
Subject: [PATCH 005/238] Reproducible Builds for libhysteria2.so (#4249)
* Patch Go use 600296
* -buildvcs=false for libhysteria2
* fix if
* fixup! Build and cache libhysteria2.so (#4226)
---
.github/workflows/build.yml | 9 ++++++++-
libhysteria2.sh | 2 +-
2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 9dd1f402..bbcf4188 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -95,7 +95,14 @@ jobs:
if: steps.cache-libhysteria2-restore.outputs.cache-hit != 'true'
uses: actions/setup-go@v5
with:
- go-mod-file: 'AndroidLibXrayLite/go.mod'
+ go-version-file: 'AndroidLibXrayLite/go.mod'
+
+ - name: Patch Go use 600296
+ if: steps.cache-libhysteria2-restore.outputs.cache-hit != 'true'
+ #https://go-review.googlesource.com/c/go/+/600296
+ run: |
+ cd "$(go env GOROOT)"
+ curl "https://go-review.googlesource.com/changes/go~600296/revisions/5/patch" | base64 -d | patch --verbose -p 1
- name: Build libhysteria2
if: steps.cache-libhysteria2-restore.outputs.cache-hit != 'true'
diff --git a/libhysteria2.sh b/libhysteria2.sh
index 5c0d6fd0..b2a72117 100644
--- a/libhysteria2.sh
+++ b/libhysteria2.sh
@@ -14,7 +14,7 @@ for target in "${targets[@]}"; do
echo "Building for ${abi} with ${ndk_target} (${goarch})"
- CC="${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/${ndk_target}-clang" CGO_ENABLED=1 CGO_LDFLAGS="-Wl,-z,max-page-size=16384" GOOS=android GOARCH=$goarch go build -o libs/$abi/libhysteria2.so -trimpath -ldflags "-s -w -buildid=" ./app
+ CC="${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/${ndk_target}-clang" CGO_ENABLED=1 CGO_LDFLAGS="-Wl,-z,max-page-size=16384" GOOS=android GOARCH=$goarch go build -o libs/$abi/libhysteria2.so -trimpath -ldflags "-s -w -buildid=" -buildvcs=false ./app
echo "Built libhysteria2.so for ${abi}"
done
From 0d0da6bfec7bbcb40dce04e14103da8a241feec0 Mon Sep 17 00:00:00 2001
From: alphax-hue3682 <191818854+alphax-hue3682@users.noreply.github.com>
Date: Sun, 12 Jan 2025 09:17:18 +0330
Subject: [PATCH 006/238] Update Persian translate (#4256)
* Update Persian translate
* Update strings.xml
---
V2rayNG/app/src/main/res/values-fa/strings.xml | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/V2rayNG/app/src/main/res/values-fa/strings.xml b/V2rayNG/app/src/main/res/values-fa/strings.xml
index c373556f..2e389827 100644
--- a/V2rayNG/app/src/main/res/values-fa/strings.xml
+++ b/V2rayNG/app/src/main/res/values-fa/strings.xml
@@ -69,7 +69,7 @@
gRPC ServiceName
TLS
اثرانگشت
- AlPN
+ Alpn
اعطای مجوز ناامن
SNI
نشانی
@@ -155,7 +155,7 @@
فعال کردن نمایش سرعت
نمایش سرعت فعلی در قسمت اعلان. \nآیکون اعلان بر اساس استفاده تغییر میکند.
- فعال کردن تجزیه و تحلیل بسته ها (SNIFFING)
+ فعال کردن تجزیه و تحلیل بسته ها (Sniffing)
استفاده از تشخیص نام دامنه (Sniff) در بسته ها (به طور پیش فرض فعال است)
فعال کردن دامنه فقط مسیر یابی (RouteOnly)
از نام دامنه (Snnifed) فقط برای مسیریابی استفاده کنید و آدرس مقصد را به عنوان IP ذخیره کنید.
@@ -179,8 +179,8 @@
DNS داخلی (اختیاری)
DNS
- DNS مستقیم هاست(فرمت: دامنه: آدرس،…)
- دامنه: آدرس، …
+ DNS مستقیم هاست (فرمت: دامنه:آدرس،…)
+ دامنه:آدرس،…
آدرس اینترنتی آزمایش تاخیر واقعی کانفیگ ها (HTTP/HTTPS)
URL
From a4edf86195dcc11049d384d21b384e7841dbf1d6 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Mon, 13 Jan 2025 12:50:35 +0800
Subject: [PATCH 007/238] Improved logcat
---
.../java/com/v2ray/ang/ui/LogcatActivity.kt | 101 +++++++++++++-----
.../com/v2ray/ang/ui/LogcatRecyclerAdapter.kt | 31 ++++++
.../src/main/res/layout/activity_logcat.xml | 25 ++---
.../main/res/layout/item_recycler_logcat.xml | 24 +++++
V2rayNG/app/src/main/res/menu/menu_logcat.xml | 10 +-
5 files changed, 143 insertions(+), 48 deletions(-)
create mode 100644 V2rayNG/app/src/main/java/com/v2ray/ang/ui/LogcatRecyclerAdapter.kt
create mode 100644 V2rayNG/app/src/main/res/layout/item_recycler_logcat.xml
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/LogcatActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/LogcatActivity.kt
index c71294eb..cf9f94bd 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/LogcatActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/LogcatActivity.kt
@@ -1,13 +1,13 @@
package com.v2ray.ang.ui
import android.os.Bundle
-import android.os.Handler
-import android.os.Looper
-import android.text.method.ScrollingMovementMethod
import android.view.Menu
import android.view.MenuItem
import android.view.View
+import androidx.appcompat.widget.SearchView
import androidx.lifecycle.lifecycleScope
+import androidx.recyclerview.widget.DividerItemDecoration
+import androidx.recyclerview.widget.LinearLayoutManager
import com.v2ray.ang.AppConfig.ANG_PACKAGE
import com.v2ray.ang.R
import com.v2ray.ang.databinding.ActivityLogcatBinding
@@ -19,9 +19,11 @@ import kotlinx.coroutines.withContext
import java.io.IOException
class LogcatActivity : BaseActivity() {
- private val binding by lazy {
- ActivityLogcatBinding.inflate(layoutInflater)
- }
+ private val binding by lazy { ActivityLogcatBinding.inflate(layoutInflater) }
+
+ var logsetsAll: MutableList = mutableListOf()
+ var logsets: MutableList = mutableListOf()
+ private val adapter by lazy { LogcatRecyclerAdapter(this) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -29,24 +31,20 @@ class LogcatActivity : BaseActivity() {
title = getString(R.string.title_logcat)
- logcat(false)
+ binding.recyclerView.setHasFixedSize(true)
+ binding.recyclerView.layoutManager = LinearLayoutManager(this)
+ binding.recyclerView.adapter = adapter
+ binding.recyclerView.addItemDecoration(DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
+
+ getLogcat()
}
- private fun logcat(shouldFlushLog: Boolean) {
+ private fun getLogcat() {
try {
binding.pbWaiting.visibility = View.VISIBLE
lifecycleScope.launch(Dispatchers.Default) {
- if (shouldFlushLog) {
- val lst = LinkedHashSet()
- lst.add("logcat")
- lst.add("-c")
- withContext(Dispatchers.IO) {
- val process = Runtime.getRuntime().exec(lst.toTypedArray())
- process.waitFor()
- }
- }
val lst = LinkedHashSet()
lst.add("logcat")
lst.add("-d")
@@ -57,15 +55,34 @@ class LogcatActivity : BaseActivity() {
val process = withContext(Dispatchers.IO) {
Runtime.getRuntime().exec(lst.toTypedArray())
}
-// val bufferedReader = BufferedReader(
-// InputStreamReader(process.inputStream))
-// val allText = bufferedReader.use(BufferedReader::readText)
- val allText = process.inputStream.bufferedReader().use { it.readText() }
+
+ val allText = process.inputStream.bufferedReader().use { it.readLines() }
launch(Dispatchers.Main) {
- binding.tvLogcat.text = allText
- binding.tvLogcat.movementMethod = ScrollingMovementMethod()
+ logsetsAll = allText.toMutableList()
+ logsets = allText.toMutableList()
+ adapter.notifyDataSetChanged()
binding.pbWaiting.visibility = View.GONE
- Handler(Looper.getMainLooper()).post { binding.svLogcat.fullScroll(View.FOCUS_DOWN) }
+ }
+ }
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+ }
+
+ private fun clearLogcat() {
+ try {
+ lifecycleScope.launch(Dispatchers.Default) {
+ val lst = LinkedHashSet()
+ lst.add("logcat")
+ lst.add("-c")
+ withContext(Dispatchers.IO) {
+ val process = Runtime.getRuntime().exec(lst.toTypedArray())
+ process.waitFor()
+ }
+ launch(Dispatchers.Main) {
+ logsetsAll.clear()
+ logsets.clear()
+ adapter.notifyDataSetChanged()
}
}
} catch (e: IOException) {
@@ -75,21 +92,51 @@ class LogcatActivity : BaseActivity() {
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_logcat, menu)
+
+ val searchItem = menu.findItem(R.id.search_view)
+ if (searchItem != null) {
+ val searchView = searchItem.actionView as SearchView
+ searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
+ override fun onQueryTextSubmit(query: String?): Boolean = false
+
+ override fun onQueryTextChange(newText: String?): Boolean {
+ filterLogs(newText)
+ return false
+ }
+ })
+ searchView.setOnCloseListener {
+ filterLogs("")
+ false
+ }
+ }
+
return super.onCreateOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
R.id.copy_all -> {
- Utils.setClipboard(this, binding.tvLogcat.text.toString())
+ Utils.setClipboard(this, logsets.joinToString("\n"))
toast(R.string.toast_success)
true
}
R.id.clear_all -> {
- logcat(true)
+ clearLogcat()
true
}
else -> super.onOptionsItemSelected(item)
}
-}
+
+ private fun filterLogs(content: String?): Boolean {
+ val key = content?.trim()
+ logsets = if (key.isNullOrEmpty()) {
+ logsetsAll.toMutableList()
+ } else {
+ logsetsAll.filter { it.contains(key) }.toMutableList()
+ }
+
+ adapter?.notifyDataSetChanged()
+ return true
+ }
+}
\ No newline at end of file
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/LogcatRecyclerAdapter.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/LogcatRecyclerAdapter.kt
new file mode 100644
index 00000000..4a9c35fa
--- /dev/null
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/LogcatRecyclerAdapter.kt
@@ -0,0 +1,31 @@
+package com.v2ray.ang.ui
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import com.v2ray.ang.databinding.ItemRecyclerLogcatBinding
+
+class LogcatRecyclerAdapter(val activity: LogcatActivity) : RecyclerView.Adapter() {
+ private var mActivity: LogcatActivity = activity
+
+ override fun getItemCount() = mActivity.logsets.size
+
+ override fun onBindViewHolder(holder: MainViewHolder, position: Int) {
+ val content = mActivity.logsets[position]
+ holder.itemSubSettingBinding.logContent.text = content
+
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainViewHolder {
+ return MainViewHolder(
+ ItemRecyclerLogcatBinding.inflate(
+ LayoutInflater.from(parent.context),
+ parent,
+ false
+ )
+ )
+ }
+
+ class MainViewHolder(val itemSubSettingBinding: ItemRecyclerLogcatBinding) : RecyclerView.ViewHolder(itemSubSettingBinding.root)
+
+}
diff --git a/V2rayNG/app/src/main/res/layout/activity_logcat.xml b/V2rayNG/app/src/main/res/layout/activity_logcat.xml
index 902116dd..56ec614a 100644
--- a/V2rayNG/app/src/main/res/layout/activity_logcat.xml
+++ b/V2rayNG/app/src/main/res/layout/activity_logcat.xml
@@ -1,11 +1,10 @@
-
+ android:layout_height="match_parent"
+ app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
-
-
diff --git a/V2rayNG/app/src/main/res/layout/item_recycler_logcat.xml b/V2rayNG/app/src/main/res/layout/item_recycler_logcat.xml
new file mode 100644
index 00000000..927a79ed
--- /dev/null
+++ b/V2rayNG/app/src/main/res/layout/item_recycler_logcat.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/V2rayNG/app/src/main/res/menu/menu_logcat.xml b/V2rayNG/app/src/main/res/menu/menu_logcat.xml
index 8044bbde..14911e57 100644
--- a/V2rayNG/app/src/main/res/menu/menu_logcat.xml
+++ b/V2rayNG/app/src/main/res/menu/menu_logcat.xml
@@ -1,14 +1,20 @@
\ No newline at end of file
From abff80ec23e2b377ddcbe6e911296bb38681bc5a Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Mon, 13 Jan 2025 12:51:26 +0800
Subject: [PATCH 008/238] Adjust UI
---
V2rayNG/app/src/main/res/layout/activity_bypass_list.xml | 1 +
V2rayNG/app/src/main/res/layout/activity_main.xml | 1 +
V2rayNG/app/src/main/res/menu/action_server.xml | 4 ++--
V2rayNG/app/src/main/res/menu/action_sub_setting.xml | 4 ++--
V2rayNG/app/src/main/res/menu/menu_asset.xml | 2 +-
V2rayNG/app/src/main/res/menu/menu_bypass_list.xml | 2 +-
V2rayNG/app/src/main/res/menu/menu_main.xml | 2 +-
V2rayNG/app/src/main/res/menu/menu_scanner.xml | 4 ++--
8 files changed, 11 insertions(+), 9 deletions(-)
diff --git a/V2rayNG/app/src/main/res/layout/activity_bypass_list.xml b/V2rayNG/app/src/main/res/layout/activity_bypass_list.xml
index 2bb05e85..2665430a 100644
--- a/V2rayNG/app/src/main/res/layout/activity_bypass_list.xml
+++ b/V2rayNG/app/src/main/res/layout/activity_bypass_list.xml
@@ -77,6 +77,7 @@
+ app:showAsAction="ifRoom" />
+ app:showAsAction="ifRoom" />
\ No newline at end of file
diff --git a/V2rayNG/app/src/main/res/menu/action_sub_setting.xml b/V2rayNG/app/src/main/res/menu/action_sub_setting.xml
index 3179cb1a..fa77aa07 100644
--- a/V2rayNG/app/src/main/res/menu/action_sub_setting.xml
+++ b/V2rayNG/app/src/main/res/menu/action_sub_setting.xml
@@ -5,10 +5,10 @@
android:id="@+id/add_config"
android:icon="@drawable/ic_add_24dp"
android:title="@string/menu_item_add_config"
- app:showAsAction="always" />
+ app:showAsAction="ifRoom" />
+ app:showAsAction="ifRoom" />
\ No newline at end of file
diff --git a/V2rayNG/app/src/main/res/menu/menu_asset.xml b/V2rayNG/app/src/main/res/menu/menu_asset.xml
index bec6fe3b..fc351ece 100644
--- a/V2rayNG/app/src/main/res/menu/menu_asset.xml
+++ b/V2rayNG/app/src/main/res/menu/menu_asset.xml
@@ -24,5 +24,5 @@
android:id="@+id/download_file"
android:icon="@drawable/ic_cloud_download_24dp"
android:title="@string/menu_item_download_file"
- app:showAsAction="always" />
+ app:showAsAction="ifRoom" />
\ No newline at end of file
diff --git a/V2rayNG/app/src/main/res/menu/menu_bypass_list.xml b/V2rayNG/app/src/main/res/menu/menu_bypass_list.xml
index 63fed36e..8d950208 100644
--- a/V2rayNG/app/src/main/res/menu/menu_bypass_list.xml
+++ b/V2rayNG/app/src/main/res/menu/menu_bypass_list.xml
@@ -6,7 +6,7 @@
android:icon="@drawable/ic_description_24dp"
android:title="@string/menu_item_search"
app:actionViewClass="androidx.appcompat.widget.SearchView"
- app:showAsAction="always" />
+ app:showAsAction="ifRoom" />
-
+ app:showAsAction="ifRoom">
\ No newline at end of file
From 2a43b523447ed554504f35cc265a4c2fa272ce08 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Mon, 13 Jan 2025 14:31:36 +0800
Subject: [PATCH 009/238] Logcat add pull-down refresh
---
V2rayNG/app/build.gradle.kts | 1 +
.../java/com/v2ray/ang/ui/LogcatActivity.kt | 19 ++++++++++-----
.../src/main/res/layout/activity_logcat.xml | 23 +++++++++----------
.../app/src/main/res/values-ar/strings.xml | 1 +
.../app/src/main/res/values-bn/strings.xml | 1 +
.../src/main/res/values-bqi-rIR/strings.xml | 1 +
.../app/src/main/res/values-fa/strings.xml | 1 +
.../app/src/main/res/values-ru/strings.xml | 1 +
.../app/src/main/res/values-vi/strings.xml | 1 +
.../src/main/res/values-zh-rCN/strings.xml | 1 +
.../src/main/res/values-zh-rTW/strings.xml | 1 +
V2rayNG/app/src/main/res/values/strings.xml | 1 +
V2rayNG/gradle/libs.versions.toml | 2 ++
13 files changed, 36 insertions(+), 18 deletions(-)
diff --git a/V2rayNG/app/build.gradle.kts b/V2rayNG/app/build.gradle.kts
index 7830347d..676dfe9a 100644
--- a/V2rayNG/app/build.gradle.kts
+++ b/V2rayNG/app/build.gradle.kts
@@ -144,6 +144,7 @@ dependencies {
implementation(libs.androidx.constraintlayout)
implementation(libs.preference.ktx)
implementation(libs.recyclerview)
+ implementation(libs.androidx.swiperefreshlayout)
// UI Libraries
implementation(libs.material)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/LogcatActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/LogcatActivity.kt
index cf9f94bd..eb13c538 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/LogcatActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/LogcatActivity.kt
@@ -3,11 +3,11 @@ package com.v2ray.ang.ui
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
-import android.view.View
import androidx.appcompat.widget.SearchView
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.v2ray.ang.AppConfig.ANG_PACKAGE
import com.v2ray.ang.R
import com.v2ray.ang.databinding.ActivityLogcatBinding
@@ -18,7 +18,8 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.IOException
-class LogcatActivity : BaseActivity() {
+
+class LogcatActivity : BaseActivity(), SwipeRefreshLayout.OnRefreshListener {
private val binding by lazy { ActivityLogcatBinding.inflate(layoutInflater) }
var logsetsAll: MutableList = mutableListOf()
@@ -34,15 +35,17 @@ class LogcatActivity : BaseActivity() {
binding.recyclerView.setHasFixedSize(true)
binding.recyclerView.layoutManager = LinearLayoutManager(this)
binding.recyclerView.adapter = adapter
- binding.recyclerView.addItemDecoration(DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
+ binding.recyclerView.addItemDecoration(DividerItemDecoration(this, DividerItemDecoration.VERTICAL))
- getLogcat()
+ binding.refreshLayout.setOnRefreshListener(this)
+
+ logsets.add(getString(R.string.pull_down_to_refresh))
}
private fun getLogcat() {
try {
- binding.pbWaiting.visibility = View.VISIBLE
+ binding.refreshLayout.isRefreshing = true
lifecycleScope.launch(Dispatchers.Default) {
val lst = LinkedHashSet()
@@ -61,7 +64,7 @@ class LogcatActivity : BaseActivity() {
logsetsAll = allText.toMutableList()
logsets = allText.toMutableList()
adapter.notifyDataSetChanged()
- binding.pbWaiting.visibility = View.GONE
+ binding.refreshLayout.isRefreshing = false
}
}
} catch (e: IOException) {
@@ -139,4 +142,8 @@ class LogcatActivity : BaseActivity() {
adapter?.notifyDataSetChanged()
return true
}
+
+ override fun onRefresh() {
+ getLogcat()
+ }
}
\ No newline at end of file
diff --git a/V2rayNG/app/src/main/res/layout/activity_logcat.xml b/V2rayNG/app/src/main/res/layout/activity_logcat.xml
index 56ec614a..7c38fd34 100644
--- a/V2rayNG/app/src/main/res/layout/activity_logcat.xml
+++ b/V2rayNG/app/src/main/res/layout/activity_logcat.xml
@@ -7,20 +7,19 @@
android:fitsSystemWindows="true"
tools:context=".ui.LogcatActivity">
-
+ android:layout_height="match_parent">
-
+
+
+
diff --git a/V2rayNG/app/src/main/res/values-ar/strings.xml b/V2rayNG/app/src/main/res/values-ar/strings.xml
index 29708941..cbe2933f 100644
--- a/V2rayNG/app/src/main/res/values-ar/strings.xml
+++ b/V2rayNG/app/src/main/res/values-ar/strings.xml
@@ -8,6 +8,7 @@
إغلاق درج التنقل
نجحت عملية ترحيل البيانات!
فشلت عملية ترحيل البيانات!
+ Please pull down to refresh!
إيقاف
diff --git a/V2rayNG/app/src/main/res/values-bn/strings.xml b/V2rayNG/app/src/main/res/values-bn/strings.xml
index e285bc0f..86e9be71 100644
--- a/V2rayNG/app/src/main/res/values-bn/strings.xml
+++ b/V2rayNG/app/src/main/res/values-bn/strings.xml
@@ -8,6 +8,7 @@
নেভিগেশন ড্রয়ার বন্ধ করুন
ডেটা স্থানান্তর সফল!
ডেটা স্থানান্তর ব্যর্থ!
+ Please pull down to refresh!
বন্ধ করুন
diff --git a/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml b/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml
index 57901b41..89efc19e 100644
--- a/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml
+++ b/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml
@@ -8,6 +8,7 @@
بستن نومگه کشاری
مووفقیت من جاگورویی داده
جاگورویی داده ٱنجوم نگرؽڌ
+ Please pull down to refresh!
واڌاشتن
diff --git a/V2rayNG/app/src/main/res/values-fa/strings.xml b/V2rayNG/app/src/main/res/values-fa/strings.xml
index 2e389827..4358ad30 100644
--- a/V2rayNG/app/src/main/res/values-fa/strings.xml
+++ b/V2rayNG/app/src/main/res/values-fa/strings.xml
@@ -8,6 +8,7 @@
بستن منو کشویی
موفقیت در انتقال داده
انتقال داده انجام نشد!
+ Please pull down to refresh!
توقف
diff --git a/V2rayNG/app/src/main/res/values-ru/strings.xml b/V2rayNG/app/src/main/res/values-ru/strings.xml
index 778db646..a4bbc55c 100644
--- a/V2rayNG/app/src/main/res/values-ru/strings.xml
+++ b/V2rayNG/app/src/main/res/values-ru/strings.xml
@@ -7,6 +7,7 @@
Закрыть панель навигации
Успешный перенос данных!
Перенос данных не выполнен!
+ Please pull down to refresh!
Остановить
diff --git a/V2rayNG/app/src/main/res/values-vi/strings.xml b/V2rayNG/app/src/main/res/values-vi/strings.xml
index 0e104e75..691ac1d8 100644
--- a/V2rayNG/app/src/main/res/values-vi/strings.xml
+++ b/V2rayNG/app/src/main/res/values-vi/strings.xml
@@ -7,6 +7,7 @@
Đóng Menu ứng dụng
Đã chuyển dữ liệu!
Không thể chuyển dữ liệu!
+ Please pull down to refresh!
Ngắt kết nối v2rayNG
diff --git a/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml b/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml
index 4b2d82f7..1a58e3ba 100644
--- a/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml
+++ b/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml
@@ -7,6 +7,7 @@
Close navigation drawer
数据迁移成功!
数据迁移失败啦!
+ 请下拉刷新!
停止
diff --git a/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml b/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml
index 4af294f3..50b6b5a8 100644
--- a/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml
+++ b/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml
@@ -7,6 +7,7 @@
關閉導覽匣
資料遷移成功!
資料遷移失敗!
+ 請下拉刷新!
停止
diff --git a/V2rayNG/app/src/main/res/values/strings.xml b/V2rayNG/app/src/main/res/values/strings.xml
index c902ac00..da1fe1a3 100644
--- a/V2rayNG/app/src/main/res/values/strings.xml
+++ b/V2rayNG/app/src/main/res/values/strings.xml
@@ -8,6 +8,7 @@
Close navigation drawer
Data migration success!
Data migration failed!
+ Please pull down to refresh!
Stop
diff --git a/V2rayNG/gradle/libs.versions.toml b/V2rayNG/gradle/libs.versions.toml
index 3bce7ffb..c789d2ff 100644
--- a/V2rayNG/gradle/libs.versions.toml
+++ b/V2rayNG/gradle/libs.versions.toml
@@ -17,6 +17,7 @@ quickieFoss = "1.13.1"
rxjava = "3.1.9"
rxandroid = "3.0.2"
rxpermissions = "0.12"
+swiperefreshlayout = "1.1.0"
toastcompat = "1.1.0"
editorkit = "2.9.0"
core = "3.5.3"
@@ -29,6 +30,7 @@ preferenceKtx = "1.2.1"
recyclerview = "1.3.2"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
+androidx-swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version.ref = "swiperefreshlayout" }
desugar_jdk_libs = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desugar_jdk_libs" }
gradle-license-plugin = { module = "com.jaredsburrows:gradle-license-plugin", version.ref = "gradleLicensePlugin" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
From 6ff3a73bf2dc90ab2a6032617d2935081d9beae9 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Mon, 13 Jan 2025 14:56:48 +0800
Subject: [PATCH 010/238] Adjust UI for subscription
---
.../res/layout/item_recycler_sub_setting.xml | 106 +++++++++---------
1 file changed, 56 insertions(+), 50 deletions(-)
diff --git a/V2rayNG/app/src/main/res/layout/item_recycler_sub_setting.xml b/V2rayNG/app/src/main/res/layout/item_recycler_sub_setting.xml
index a2ad1817..663c53ab 100644
--- a/V2rayNG/app/src/main/res/layout/item_recycler_sub_setting.xml
+++ b/V2rayNG/app/src/main/res/layout/item_recycler_sub_setting.xml
@@ -44,70 +44,76 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_margin_spacing"
- android:lines="1"
+ android:lines="2"
android:textAppearance="@style/TextAppearance.AppCompat.Small" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
From 7e99b1ac78e6bab19554c21dd958eb80ea2e9526 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Mon, 13 Jan 2025 15:04:01 +0800
Subject: [PATCH 011/238] up 1.9.32
---
V2rayNG/app/build.gradle.kts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/V2rayNG/app/build.gradle.kts b/V2rayNG/app/build.gradle.kts
index 676dfe9a..a51d4cd3 100644
--- a/V2rayNG/app/build.gradle.kts
+++ b/V2rayNG/app/build.gradle.kts
@@ -12,8 +12,8 @@ android {
applicationId = "com.v2ray.ang"
minSdk = 21
targetSdk = 35
- versionCode = 627
- versionName = "1.9.31"
+ versionCode = 628
+ versionName = "1.9.32"
multiDexEnabled = true
val abiFilterList = (properties["ABI_FILTERS"] as? String)?.split(';')
From d8d3767798dad9300f4cbe33303fca7b0be77717 Mon Sep 17 00:00:00 2001
From: alphax-hue3682 <191818854+alphax-hue3682@users.noreply.github.com>
Date: Tue, 14 Jan 2025 05:23:57 +0330
Subject: [PATCH 012/238] Update Persian translate (#4264)
---
V2rayNG/app/src/main/res/values-fa/strings.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/V2rayNG/app/src/main/res/values-fa/strings.xml b/V2rayNG/app/src/main/res/values-fa/strings.xml
index 4358ad30..a7d7d227 100644
--- a/V2rayNG/app/src/main/res/values-fa/strings.xml
+++ b/V2rayNG/app/src/main/res/values-fa/strings.xml
@@ -8,7 +8,7 @@
بستن منو کشویی
موفقیت در انتقال داده
انتقال داده انجام نشد!
- Please pull down to refresh!
+ لطفاً برای تازه کردن، پایین بکشید!
توقف
From e55e069fe3c4502ce1a93755c4a2cd3ff9f21ed2 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Thu, 16 Jan 2025 14:44:19 +0800
Subject: [PATCH 013/238] Add bandwidth to hysteria2 settings
https://github.com/2dust/v2rayNG/issues/4261
---
.../java/com/v2ray/ang/dto/Hysteria2Bean.kt | 8 +++-
.../java/com/v2ray/ang/dto/ProfileItem.kt | 2 +
.../java/com/v2ray/ang/fmt/Hysteria2Fmt.kt | 7 ++++
.../java/com/v2ray/ang/ui/ServerActivity.kt | 6 +++
.../res/layout/activity_server_hysteria2.xml | 38 +++++++++++++++++++
.../app/src/main/res/values-ar/strings.xml | 2 +
.../app/src/main/res/values-bn/strings.xml | 2 +
.../src/main/res/values-bqi-rIR/strings.xml | 2 +
.../app/src/main/res/values-fa/strings.xml | 2 +
.../app/src/main/res/values-ru/strings.xml | 2 +
.../app/src/main/res/values-vi/strings.xml | 2 +
.../src/main/res/values-zh-rCN/strings.xml | 2 +
.../src/main/res/values-zh-rTW/strings.xml | 2 +
V2rayNG/app/src/main/res/values/strings.xml | 2 +
14 files changed, 78 insertions(+), 1 deletion(-)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/Hysteria2Bean.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/Hysteria2Bean.kt
index 89900fb8..c4fc6582 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/Hysteria2Bean.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/Hysteria2Bean.kt
@@ -9,6 +9,7 @@ data class Hysteria2Bean(
val http: Socks5Bean? = null,
val tls: TlsBean? = null,
val transport: TransportBean? = null,
+ val bandwidth: BandwidthBean? = null,
) {
data class ObfsBean(
val type: String?,
@@ -37,4 +38,9 @@ data class Hysteria2Bean(
val hopInterval: String?,
)
}
-}
+
+ data class BandwidthBean(
+ val down: String?,
+ val up: String?,
+ )
+}
\ No newline at end of file
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/ProfileItem.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/ProfileItem.kt
index 1a0c7807..7a1f7346 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/ProfileItem.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/ProfileItem.kt
@@ -55,6 +55,8 @@ data class ProfileItem(
var portHopping: String? = null,
var portHoppingInterval: String? = null,
var pinSHA256: String? = null,
+ var bandwidthDown: String? = null,
+ var bandwidthUp: String? = null,
) {
companion object {
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/Hysteria2Fmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/Hysteria2Fmt.kt
index 0aad9c88..1c5dc99d 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/Hysteria2Fmt.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/Hysteria2Fmt.kt
@@ -85,6 +85,12 @@ object Hysteria2Fmt : FmtBase() {
)
)
+ val bandwidth = if (config.bandwidthDown.isNullOrEmpty() || config.bandwidthUp.isNullOrEmpty()) null else
+ Hysteria2Bean.BandwidthBean(
+ down = config.bandwidthDown,
+ up = config.bandwidthUp,
+ )
+
val server =
if (config.portHopping.isNullOrEmpty())
config.getServerAddressAndPort()
@@ -96,6 +102,7 @@ object Hysteria2Fmt : FmtBase() {
auth = config.password,
obfs = obfs,
transport = transport,
+ bandwidth = bandwidth,
socks5 = Hysteria2Bean.Socks5Bean(
listen = "$LOOPBACK:${socksPort}",
),
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ServerActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ServerActivity.kt
index 7cb66933..9d0a6b0e 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ServerActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ServerActivity.kt
@@ -126,6 +126,8 @@ class ServerActivity : BaseActivity() {
private val et_port_hop: EditText? by lazy { findViewById(R.id.et_port_hop) }
private val et_port_hop_interval: EditText? by lazy { findViewById(R.id.et_port_hop_interval) }
private val et_pinsha256: EditText? by lazy { findViewById(R.id.et_pinsha256) }
+ private val et_bandwidth_down: EditText? by lazy { findViewById(R.id.et_bandwidth_down) }
+ private val et_bandwidth_up: EditText? by lazy { findViewById(R.id.et_bandwidth_up) }
private val et_extra: EditText? by lazy { findViewById(R.id.et_extra) }
private val layout_extra: LinearLayout? by lazy { findViewById(R.id.layout_extra) }
@@ -334,6 +336,8 @@ class ServerActivity : BaseActivity() {
et_port_hop?.text = Utils.getEditable(config.portHopping)
et_port_hop_interval?.text = Utils.getEditable(config.portHoppingInterval)
et_pinsha256?.text = Utils.getEditable(config.pinSHA256)
+ et_bandwidth_down?.text = Utils.getEditable(config.bandwidthDown)
+ et_bandwidth_up?.text = Utils.getEditable(config.bandwidthUp)
}
val securityEncryptions =
if (config.configType == EConfigType.SHADOWSOCKS) shadowsocksSecuritys else securitys
@@ -513,6 +517,8 @@ class ServerActivity : BaseActivity() {
config.portHopping = et_port_hop?.text?.toString()
config.portHoppingInterval = et_port_hop_interval?.text?.toString()
config.pinSHA256 = et_pinsha256?.text?.toString()
+ config.bandwidthDown = et_bandwidth_down?.text?.toString()
+ config.bandwidthUp = et_bandwidth_up?.text?.toString()
}
}
diff --git a/V2rayNG/app/src/main/res/layout/activity_server_hysteria2.xml b/V2rayNG/app/src/main/res/layout/activity_server_hysteria2.xml
index 24a120a0..a0228555 100644
--- a/V2rayNG/app/src/main/res/layout/activity_server_hysteria2.xml
+++ b/V2rayNG/app/src/main/res/layout/activity_server_hysteria2.xml
@@ -90,6 +90,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Port Hopping
Port Hopping Interval
pinSHA256
+ Bandwidth down (units)
+ Bandwidth up (units)
XHTTP Mode
XHTTP Extra raw JSON, format: { XHTTPObject }
diff --git a/V2rayNG/app/src/main/res/values-bn/strings.xml b/V2rayNG/app/src/main/res/values-bn/strings.xml
index 86e9be71..272ee1ea 100644
--- a/V2rayNG/app/src/main/res/values-bn/strings.xml
+++ b/V2rayNG/app/src/main/res/values-bn/strings.xml
@@ -120,6 +120,8 @@
Port Hopping
Port Hopping Interval
pinSHA256
+ Bandwidth down (units)
+ Bandwidth up (units)
XHTTP Mode
XHTTP Extra raw JSON, format: { XHTTPObject }
diff --git a/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml b/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml
index 89efc19e..b6151b4e 100644
--- a/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml
+++ b/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml
@@ -120,6 +120,8 @@
پورت گوم (درگا سرورن ز نۊ هؽل اکونه)
فاسله پورت گوم (سانیه)
pinSHA256
+ Bandwidth down (units)
+ Bandwidth up (units)
هالت XHTTP
XHTTP Extra خام JSON، قالوو: { XHTTPObject }
diff --git a/V2rayNG/app/src/main/res/values-fa/strings.xml b/V2rayNG/app/src/main/res/values-fa/strings.xml
index a7d7d227..3a30f440 100644
--- a/V2rayNG/app/src/main/res/values-fa/strings.xml
+++ b/V2rayNG/app/src/main/res/values-fa/strings.xml
@@ -115,6 +115,8 @@
پورت پرش (درگاه سرور را بازنویسی می کند)
فاصله پورت پرش (ثانیه)
pinSHA256
+ Bandwidth down (units)
+ Bandwidth up (units)
حالت XHTTP
خام JSON XHTTP Extra، قالب: { XHTTPObject }
diff --git a/V2rayNG/app/src/main/res/values-ru/strings.xml b/V2rayNG/app/src/main/res/values-ru/strings.xml
index a4bbc55c..3dcc8704 100644
--- a/V2rayNG/app/src/main/res/values-ru/strings.xml
+++ b/V2rayNG/app/src/main/res/values-ru/strings.xml
@@ -119,6 +119,8 @@
Смена портов (переопределяет порт)
Интервал смены портов
pinSHA256
+ Bandwidth down (units)
+ Bandwidth up (units)
Режим XHTTP
Необработанный JSON XHTTP Extra, формат: { XHTTPObject }
diff --git a/V2rayNG/app/src/main/res/values-vi/strings.xml b/V2rayNG/app/src/main/res/values-vi/strings.xml
index 691ac1d8..f0acddf4 100644
--- a/V2rayNG/app/src/main/res/values-vi/strings.xml
+++ b/V2rayNG/app/src/main/res/values-vi/strings.xml
@@ -114,6 +114,8 @@
Port Hopping
Port Hopping Interval
pinSHA256
+ Bandwidth down (units)
+ Bandwidth up (units)
XHTTP Mode
XHTTP Extra raw JSON, format: { XHTTPObject }
diff --git a/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml b/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml
index 1a58e3ba..3542d89f 100644
--- a/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml
+++ b/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml
@@ -114,6 +114,8 @@
跳跃端口(会覆盖服务器端口)
端口跳跃间隔(秒)
SHA256证书指纹
+ 带宽下行 (单位)
+ 带宽上行 (单位)
XHTTP 模式
XHTTP Extra 原始 JSON,格式: { XHTTPObject }
diff --git a/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml b/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml
index 50b6b5a8..05fa03b5 100644
--- a/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml
+++ b/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml
@@ -114,6 +114,8 @@
跳躍連接埠(會覆蓋伺服器連接埠)
連接埠跳躍間隔(秒)
SHA256憑證指紋
+ 頻寬下行 (單位)
+ 頻寬上行 (單位)
XHTTP 模式
XHTTP Extra 原始 JSON,格式: { XHTTPObject }
diff --git a/V2rayNG/app/src/main/res/values/strings.xml b/V2rayNG/app/src/main/res/values/strings.xml
index da1fe1a3..efdee8df 100644
--- a/V2rayNG/app/src/main/res/values/strings.xml
+++ b/V2rayNG/app/src/main/res/values/strings.xml
@@ -120,6 +120,8 @@
Port Hopping(will override the port)
Port Hopping Interval
pinSHA256
+ Bandwidth down (units)
+ Bandwidth up (units)
XHTTP Mode
XHTTP Extra raw JSON, format: { XHTTPObject }
From f4e088131b3666369a6e811ab42030284303330a Mon Sep 17 00:00:00 2001
From: alphax-hue3682 <191818854+alphax-hue3682@users.noreply.github.com>
Date: Thu, 16 Jan 2025 11:26:23 +0330
Subject: [PATCH 014/238] Update Persian translate (#4269)
---
V2rayNG/app/src/main/res/values-fa/strings.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/V2rayNG/app/src/main/res/values-fa/strings.xml b/V2rayNG/app/src/main/res/values-fa/strings.xml
index 3a30f440..7e795239 100644
--- a/V2rayNG/app/src/main/res/values-fa/strings.xml
+++ b/V2rayNG/app/src/main/res/values-fa/strings.xml
@@ -115,8 +115,8 @@
پورت پرش (درگاه سرور را بازنویسی می کند)
فاصله پورت پرش (ثانیه)
pinSHA256
- Bandwidth down (units)
- Bandwidth up (units)
+ کاهش پهنای باند (واحد)
+ افزایش پهنای باند (واحد)
حالت XHTTP
خام JSON XHTTP Extra، قالب: { XHTTPObject }
From 9e3b92014a1b06644bd85f403eb8525c208b82d9 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Fri, 17 Jan 2025 13:53:35 +0800
Subject: [PATCH 015/238] logcat content reversed
---
V2rayNG/app/src/main/java/com/v2ray/ang/ui/LogcatActivity.kt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/LogcatActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/LogcatActivity.kt
index eb13c538..b275c348 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/LogcatActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/LogcatActivity.kt
@@ -59,7 +59,7 @@ class LogcatActivity : BaseActivity(), SwipeRefreshLayout.OnRefreshListener {
Runtime.getRuntime().exec(lst.toTypedArray())
}
- val allText = process.inputStream.bufferedReader().use { it.readLines() }
+ val allText = process.inputStream.bufferedReader().use { it.readLines() }.reversed()
launch(Dispatchers.Main) {
logsetsAll = allText.toMutableList()
logsets = allText.toMutableList()
From c3786d434eb4aff1d611a20481ea2f32ef09e824 Mon Sep 17 00:00:00 2001
From: alphax-hue3682 <191818854+alphax-hue3682@users.noreply.github.com>
Date: Sun, 19 Jan 2025 14:06:46 +0330
Subject: [PATCH 016/238] remove patch (#4279)
remove patch
---
.github/workflows/build.yml | 7 -------
1 file changed, 7 deletions(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index bbcf4188..1aa753f4 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -97,13 +97,6 @@ jobs:
with:
go-version-file: 'AndroidLibXrayLite/go.mod'
- - name: Patch Go use 600296
- if: steps.cache-libhysteria2-restore.outputs.cache-hit != 'true'
- #https://go-review.googlesource.com/c/go/+/600296
- run: |
- cd "$(go env GOROOT)"
- curl "https://go-review.googlesource.com/changes/go~600296/revisions/5/patch" | base64 -d | patch --verbose -p 1
-
- name: Build libhysteria2
if: steps.cache-libhysteria2-restore.outputs.cache-hit != 'true'
run: |
From d7d3b23cea27a7436bc757a9f981cd64cfdc02a5 Mon Sep 17 00:00:00 2001
From: solokot
Date: Sat, 25 Jan 2025 05:02:51 +0300
Subject: [PATCH 017/238] Update Russian translation (#4281)
---
V2rayNG/app/src/main/res/values-ru/strings.xml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/V2rayNG/app/src/main/res/values-ru/strings.xml b/V2rayNG/app/src/main/res/values-ru/strings.xml
index 3dcc8704..52d79a6f 100644
--- a/V2rayNG/app/src/main/res/values-ru/strings.xml
+++ b/V2rayNG/app/src/main/res/values-ru/strings.xml
@@ -7,7 +7,7 @@
Закрыть панель навигации
Успешный перенос данных!
Перенос данных не выполнен!
- Please pull down to refresh!
+ Потяните вниз для обновления!
Остановить
@@ -119,8 +119,8 @@
Смена портов (переопределяет порт)
Интервал смены портов
pinSHA256
- Bandwidth down (units)
- Bandwidth up (units)
+ Входящая пропускная способность (единицы)
+ Исходящая пропускная способность (единицы)
Режим XHTTP
Необработанный JSON XHTTP Extra, формат: { XHTTPObject }
From 10b849ef097141d008c55d1a7bc9857e17bc8b64 Mon Sep 17 00:00:00 2001
From: kore kas nadar <63148255+korekasnadar@users.noreply.github.com>
Date: Sat, 25 Jan 2025 05:33:01 +0330
Subject: [PATCH 018/238] Update Luri Bakhtiari translation (#4286)
---
V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml b/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml
index b6151b4e..7e31d20f 100644
--- a/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml
+++ b/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml
@@ -120,8 +120,8 @@
پورت گوم (درگا سرورن ز نۊ هؽل اکونه)
فاسله پورت گوم (سانیه)
pinSHA256
- Bandwidth down (units)
- Bandwidth up (units)
+ ب لم ٱووڌن پئنا باند (واهڌ)
+ وا روء رئڌن پئنا باند (واهڌ)
هالت XHTTP
XHTTP Extra خام JSON، قالوو: { XHTTPObject }
@@ -191,11 +191,11 @@
نشۊوی اینترنتی
هشتن منپیزا ز شبکه مهلی
- پوی دسگایل ترن وا آدرس IP ایسا، ز ر socks/http و پروکسی منپیز بۊن، تینا من شبکه قابل اعتماد فعال بۊ تا ز منپیز ؛یر موجاز جلو گری بۊ.
+ پوی دسگایل ترن وا آدرس IP ایسا، ز ر socks/http و پروکسی منپیز بۊن، تینا من شبکه قابل اعتماد فعال بۊ تا ز منپیز غیر موجاز جلو گری بۊ.
منپیزا ز شبکه مهلی ن موجار کۊنین، موتمعن بۊین ک من ی شبکه قابل ائتماڌ هڌین.
اجازه نا ٱمن
- مجال و کار بردن TLS ب تۉر پؽش فرز، موجوز نا ٱمن فعال هڌ.
+ مجال و کار بوردن TLS ب تۉر پؽش فرز، موجوز نا ٱمن فعال هڌ.
پورت پروکسی مهلی
پورت پروکسی مهلی
@@ -348,4 +348,4 @@
- دور زیڌه نبۊ
-
\ No newline at end of file
+
From 0fb705e1e218c475787226eb8a667b91b08809e2 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Sat, 25 Jan 2025 10:28:23 +0800
Subject: [PATCH 019/238] Update libs.versions.toml
---
V2rayNG/gradle/libs.versions.toml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/V2rayNG/gradle/libs.versions.toml b/V2rayNG/gradle/libs.versions.toml
index c789d2ff..4310536b 100644
--- a/V2rayNG/gradle/libs.versions.toml
+++ b/V2rayNG/gradle/libs.versions.toml
@@ -9,7 +9,7 @@ junitVersion = "1.2.1"
espressoCore = "3.6.1"
appcompat = "1.7.0"
material = "1.12.0"
-activity = "1.9.3"
+activity = "1.10.0"
constraintlayout = "2.2.0"
mmkvStatic = "1.3.11"
gson = "2.11.0"
@@ -27,7 +27,7 @@ multidex = "2.0.1"
mockitoMockitoInline = "4.0.0"
flexbox = "3.0.0"
preferenceKtx = "1.2.1"
-recyclerview = "1.3.2"
+recyclerview = "1.4.0"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
androidx-swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version.ref = "swiperefreshlayout" }
From d51a4d7a7eb73f0fc64abc813e0cec5c119adf63 Mon Sep 17 00:00:00 2001
From: alphax-hue3682 <191818854+alphax-hue3682@users.noreply.github.com>
Date: Sat, 25 Jan 2025 09:07:47 +0330
Subject: [PATCH 020/238] Update libs.versions.toml (#4291)
* Update libs.versions.toml
* Update gradle-wrapper.properties
* Update libs.versions.toml
* Update libs.versions.toml
---
V2rayNG/gradle/libs.versions.toml | 5 ++---
V2rayNG/gradle/wrapper/gradle-wrapper.properties | 2 +-
2 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/V2rayNG/gradle/libs.versions.toml b/V2rayNG/gradle/libs.versions.toml
index 4310536b..43e11018 100644
--- a/V2rayNG/gradle/libs.versions.toml
+++ b/V2rayNG/gradle/libs.versions.toml
@@ -1,5 +1,5 @@
[versions]
-agp = "8.7.3"
+agp = "8.8.0"
desugar_jdk_libs = "2.1.4"
gradleLicensePlugin = "0.9.8"
kotlin = "2.1.0"
@@ -14,7 +14,7 @@ constraintlayout = "2.2.0"
mmkvStatic = "1.3.11"
gson = "2.11.0"
quickieFoss = "1.13.1"
-rxjava = "3.1.9"
+rxjava = "3.1.10"
rxandroid = "3.0.2"
rxpermissions = "0.12"
swiperefreshlayout = "1.1.0"
@@ -66,4 +66,3 @@ preference-ktx = { module = "androidx.preference:preference-ktx", version.ref =
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
android-library = { id = "com.android.library", version.ref = "agp" }
-
diff --git a/V2rayNG/gradle/wrapper/gradle-wrapper.properties b/V2rayNG/gradle/wrapper/gradle-wrapper.properties
index 7e8d8ede..13e057c9 100644
--- a/V2rayNG/gradle/wrapper/gradle-wrapper.properties
+++ b/V2rayNG/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
#Thu Nov 14 12:42:51 BDT 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
From eb22c7f3030fb420bf3dd46a28b1ef893f3f19bd Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Sat, 25 Jan 2025 13:40:08 +0800
Subject: [PATCH 021/238] up 1.9.33
---
V2rayNG/app/build.gradle.kts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/V2rayNG/app/build.gradle.kts b/V2rayNG/app/build.gradle.kts
index a51d4cd3..d84ae79f 100644
--- a/V2rayNG/app/build.gradle.kts
+++ b/V2rayNG/app/build.gradle.kts
@@ -12,8 +12,8 @@ android {
applicationId = "com.v2ray.ang"
minSdk = 21
targetSdk = 35
- versionCode = 628
- versionName = "1.9.32"
+ versionCode = 629
+ versionName = "1.9.33"
multiDexEnabled = true
val abiFilterList = (properties["ABI_FILTERS"] as? String)?.split(';')
From 04c98326b23c78fbb99746e6a71e998dc2aef58d Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Thu, 30 Jan 2025 19:55:47 +0800
Subject: [PATCH 022/238] up 1.9.34
---
V2rayNG/app/build.gradle.kts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/V2rayNG/app/build.gradle.kts b/V2rayNG/app/build.gradle.kts
index d84ae79f..58dab903 100644
--- a/V2rayNG/app/build.gradle.kts
+++ b/V2rayNG/app/build.gradle.kts
@@ -12,8 +12,8 @@ android {
applicationId = "com.v2ray.ang"
minSdk = 21
targetSdk = 35
- versionCode = 629
- versionName = "1.9.33"
+ versionCode = 630
+ versionName = "1.9.34"
multiDexEnabled = true
val abiFilterList = (properties["ABI_FILTERS"] as? String)?.split(';')
From 3a0f2687e97b084f70f7c79ced72046d5a3bdaee Mon Sep 17 00:00:00 2001
From: alphax-hue3682 <191818854+alphax-hue3682@users.noreply.github.com>
Date: Thu, 30 Jan 2025 15:50:26 +0330
Subject: [PATCH 023/238] Update_Submodules (#4292)
* UpdateSubmodules
* Update _Submodules
---
AndroidLibXrayLite | 2 +-
hysteria | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/AndroidLibXrayLite b/AndroidLibXrayLite
index 664c3892..afcc3ff3 160000
--- a/AndroidLibXrayLite
+++ b/AndroidLibXrayLite
@@ -1 +1 @@
-Subproject commit 664c3892e22aab6501f9f5c5fb5473da605528a0
+Subproject commit afcc3ff300eb47018cddeac95311db7ef9c43c19
diff --git a/hysteria b/hysteria
index 15e31d48..8c052175 160000
--- a/hysteria
+++ b/hysteria
@@ -1 +1 @@
-Subproject commit 15e31d48a09af0835773bd5c62cedbb3fc0e351d
+Subproject commit 8c0521759079884530ad24a4b9056c00fe93d371
From e8d2c6214b0a4efd483c2f8b4a49b0b8d043f1f1 Mon Sep 17 00:00:00 2001
From: alphax-hue3682 <191818854+alphax-hue3682@users.noreply.github.com>
Date: Thu, 30 Jan 2025 16:16:01 +0330
Subject: [PATCH 024/238] Update dependencies (#4301)
* Update dependencies
* Update dependencies
---
AndroidLibXrayLite | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/AndroidLibXrayLite b/AndroidLibXrayLite
index afcc3ff3..78e06f32 160000
--- a/AndroidLibXrayLite
+++ b/AndroidLibXrayLite
@@ -1 +1 @@
-Subproject commit afcc3ff300eb47018cddeac95311db7ef9c43c19
+Subproject commit 78e06f32b4a88a4e8cb7364abd4a1f8389a1da8b
From 2bc31a10c5b60f882f92ad99cb252d62c9289db0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E1=A1=A0=E1=A0=B5=E1=A1=A0=E1=A1=B3=20=E1=A1=A0=E1=A0=B5?=
=?UTF-8?q?=E1=A1=A0=20=E1=A0=AE=E1=A0=A0=E1=A0=A8=E1=A1=A9=E1=A0=8B?=
=?UTF-8?q?=E1=A0=A0=E1=A0=A8?=
<125150101+UjuiUjuMandan@users.noreply.github.com>
Date: Fri, 31 Jan 2025 13:54:08 +0800
Subject: [PATCH 025/238] rm AndroidLibV2rayLite (#4303)
---
AndroidLibV2rayLite/README.md | 20 --------------------
1 file changed, 20 deletions(-)
delete mode 100644 AndroidLibV2rayLite/README.md
diff --git a/AndroidLibV2rayLite/README.md b/AndroidLibV2rayLite/README.md
deleted file mode 100644
index 118eefcf..00000000
--- a/AndroidLibV2rayLite/README.md
+++ /dev/null
@@ -1,20 +0,0 @@
-# AndroidLibV2rayLite
-
-### Preparation
-- latest Ubuntu environment
-- At lease 30G free space
-- Get Repo [AndroidLibV2rayLite](https://github.com/2dust/AndroidLibV2rayLite) or [AndroidLibXrayLite](https://github.com/2dust/AndroidLibXrayLite)
-### Prepare Go
-- Go to https://golang.org/doc/install and install latest go
-- Make sure `go version` works as expected
-### Prepare gomobile
-- Go to https://pkg.go.dev/golang.org/x/mobile/cmd/gomobile and install gomobile
-- export PATH=$PATH:~/go/bin
-- Make sure `gomobile init` works as expected
-### Prepare NDK
-- Go to https://developer.android.com/ndk/downloads and install latest NDK
-- export PATH=$PATH:
-- Make sure `ndk-build -v` works as expected
-### Make
-- sudo apt install make
-- Read and understand [build script](https://github.com/2dust/AndroidLibV2rayLite/blob/master/Makefile)
From 4a653d49358caf642669c84f6d3a8bd4f93491c6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E1=A1=A0=E1=A0=B5=E1=A1=A0=E1=A1=B3=20=E1=A1=A0=E1=A0=B5?=
=?UTF-8?q?=E1=A1=A0=20=E1=A0=AE=E1=A0=A0=E1=A0=A8=E1=A1=A9=E1=A0=8B?=
=?UTF-8?q?=E1=A0=A0=E1=A0=A8?=
<125150101+UjuiUjuMandan@users.noreply.github.com>
Date: Fri, 31 Jan 2025 13:58:59 +0800
Subject: [PATCH 026/238] Fix badvpn (#4302)
* copying from https://github.com/2dust/AndroidLibXrayLite/commit/df181a30659867653270d7306780da5aa7c03a6d
* add missing includes of https://github.com/XTLS/badvpn/commit/dc99ade18dbd32a286bb4159bf9a9779c21a66ed
* update workflow
* fixup! update workflow
---
.github/workflows/build.yml | 9 ++-
.gitmodules | 6 ++
badvpn | 1 +
compile-tun2socks.sh | 33 ++++++++++
libancillary | 1 +
tun2socks.mk | 124 ++++++++++++++++++++++++++++++++++++
6 files changed, 169 insertions(+), 5 deletions(-)
create mode 160000 badvpn
create mode 100644 compile-tun2socks.sh
create mode 160000 libancillary
create mode 100644 tun2socks.mk
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 1aa753f4..999b84a2 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -26,7 +26,7 @@ jobs:
uses: actions/cache/restore@v4
with:
path: ${{ github.workspace }}/AndroidLibXrayLite/libs
- key: libtun2socks-${{ runner.os }}-${{ hashFiles('.git/modules/AndroidLibXrayLite/modules/badvpn/HEAD') }}-${{ hashFiles('.git/modules/AndroidLibXrayLite/modules/libancillary/HEAD') }}
+ key: libtun2socks-${{ runner.os }}-${{ hashFiles('.git/modules/badvpn/refs/heads/main') }}-${{ hashFiles('.git/modules/libancillary/refs/heads/shadowsocks-android') }}
- name: Setup Android NDK
uses: nttld/setup-ndk@v1
@@ -51,7 +51,6 @@ jobs:
- name: Build libtun2socks
if: steps.cache-libtun2socks-restore.outputs.cache-hit != 'true'
run: |
- cd ${{ github.workspace }}/AndroidLibXrayLite
bash compile-tun2socks.sh
tar -xvzf libtun2socks.so.tgz
env:
@@ -61,12 +60,12 @@ jobs:
if: steps.cache-libtun2socks-restore.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
- path: ${{ github.workspace }}/AndroidLibXrayLite/libs
- key: libtun2socks-${{ runner.os }}-${{ hashFiles('.git/modules/AndroidLibXrayLite/modules/badvpn/HEAD') }}-${{ hashFiles('.git/modules/AndroidLibXrayLite/modules/libancillary/HEAD') }}
+ path: ${{ github.workspace }}/libs
+ key: libtun2socks-${{ runner.os }}-${{ hashFiles('.git/modules/badvpn/refs/heads/main') }}-${{ hashFiles('.git/modules/libancillary/refs/heads/shadowsocks-android') }}
- name: Copy libtun2socks
run: |
- cp -r ${{ github.workspace }}/AndroidLibXrayLite/libs ${{ github.workspace }}/V2rayNG/app
+ cp -r ${{ github.workspace }}/libs ${{ github.workspace }}/V2rayNG/app
- name: Fetch AndroidLibXrayLite tag
run: |
diff --git a/.gitmodules b/.gitmodules
index 5823c38a..2bdafca3 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -4,3 +4,9 @@
[submodule "AndroidLibXrayLite"]
path = AndroidLibXrayLite
url = https://github.com/2dust/AndroidLibXrayLite
+[submodule "badvpn"]
+ path = badvpn
+ url = https://github.com/XTLS/badvpn
+[submodule "libancillary"]
+ path = libancillary
+ url = https://github.com/shadowsocks/libancillary
diff --git a/badvpn b/badvpn
new file mode 160000
index 00000000..3cb49ab8
--- /dev/null
+++ b/badvpn
@@ -0,0 +1 @@
+Subproject commit 3cb49ab81072dc84a27011937659d31467d18a0a
diff --git a/compile-tun2socks.sh b/compile-tun2socks.sh
new file mode 100644
index 00000000..11bea0d2
--- /dev/null
+++ b/compile-tun2socks.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+set -o errexit
+set -o pipefail
+set -o nounset
+# Set magic variables for current file & dir
+__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+__file="${__dir}/$(basename "${BASH_SOURCE[0]}")"
+__base="$(basename ${__file} .sh)"
+if [[ ! -d $NDK_HOME ]]; then
+ echo "Android NDK: NDK_HOME not found. please set env \$NDK_HOME"
+ exit 1
+fi
+TMPDIR=$(mktemp -d)
+clear_tmp () {
+ rm -rf $TMPDIR
+}
+trap 'echo -e "Aborted, error $? in command: $BASH_COMMAND"; trap ERR; clear_tmp; exit 1' ERR INT
+install -m644 $__dir/tun2socks.mk $TMPDIR/
+pushd $TMPDIR
+ln -s $__dir/badvpn badvpn
+ln -s $__dir/libancillary libancillary
+$NDK_HOME/ndk-build \
+ NDK_PROJECT_PATH=. \
+ APP_BUILD_SCRIPT=./tun2socks.mk \
+ APP_ABI=all \
+ APP_PLATFORM=android-19 \
+ NDK_LIBS_OUT=$TMPDIR/libs \
+ NDK_OUT=$TMPDIR/tmp \
+ APP_SHORT_COMMANDS=false LOCAL_SHORT_COMMANDS=false -B -j4 \
+ LOCAL_LDFLAGS=-Wl,--build-id=none
+tar cvfz $__dir/libtun2socks.so.tgz libs
+popd
+rm -rf $TMPDIR
diff --git a/libancillary b/libancillary
new file mode 160000
index 00000000..232d69a5
--- /dev/null
+++ b/libancillary
@@ -0,0 +1 @@
+Subproject commit 232d69a5ebb4461b572bd3f3b97088091e01c243
diff --git a/tun2socks.mk b/tun2socks.mk
new file mode 100644
index 00000000..04ac8df3
--- /dev/null
+++ b/tun2socks.mk
@@ -0,0 +1,124 @@
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+LOCAL_PATH := $(call my-dir)
+ROOT_PATH := $(LOCAL_PATH)
+########################################################
+## libancillary
+########################################################
+include $(CLEAR_VARS)
+ANCILLARY_SOURCE := fd_recv.c fd_send.c
+LOCAL_MODULE := libancillary
+#LOCAL_CFLAGS += -I$(LOCAL_PATH)/libancillary
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/libancillary
+LOCAL_SRC_FILES := $(addprefix libancillary/, $(ANCILLARY_SOURCE))
+include $(BUILD_STATIC_LIBRARY)
+########################################################
+## tun2socks
+########################################################
+include $(CLEAR_VARS)
+LOCAL_CFLAGS := -std=gnu99
+LOCAL_CFLAGS += -DBADVPN_THREADWORK_USE_PTHREAD -DBADVPN_LINUX -DBADVPN_BREACTOR_BADVPN -D_GNU_SOURCE
+LOCAL_CFLAGS += -DBADVPN_USE_SIGNALFD -DBADVPN_USE_EPOLL
+LOCAL_CFLAGS += -DBADVPN_LITTLE_ENDIAN -DBADVPN_THREAD_SAFE
+LOCAL_CFLAGS += -DNDEBUG -DANDROID
+LOCAL_CFLAGS += -I
+LOCAL_STATIC_LIBRARIES := libancillary
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/badvpn/libancillary \
+ $(LOCAL_PATH)/badvpn/lwip/src/include/ipv4 \
+ $(LOCAL_PATH)/badvpn/lwip/src/include/ipv6 \
+ $(LOCAL_PATH)/badvpn/lwip/src/include \
+ $(LOCAL_PATH)/badvpn/lwip/custom \
+ $(LOCAL_PATH)/badvpn \
+ $(LOCAL_PATH)/libancillary
+TUN2SOCKS_SOURCES := \
+ base/BLog_syslog.c \
+ system/BReactor_badvpn.c \
+ system/BSignal.c \
+ system/BConnection_common.c \
+ system/BConnection_unix.c \
+ system/BTime.c \
+ system/BUnixSignal.c \
+ system/BNetwork.c \
+ system/BDatagram_common.c \
+ system/BDatagram_unix.c \
+ flow/StreamRecvInterface.c \
+ flow/PacketRecvInterface.c \
+ flow/PacketPassInterface.c \
+ flow/StreamPassInterface.c \
+ flow/SinglePacketBuffer.c \
+ flow/BufferWriter.c \
+ flow/PacketBuffer.c \
+ flow/PacketStreamSender.c \
+ flow/PacketPassConnector.c \
+ flow/PacketProtoFlow.c \
+ flow/PacketPassFairQueue.c \
+ flow/PacketProtoEncoder.c \
+ flow/PacketProtoDecoder.c \
+ socksclient/BSocksClient.c \
+ tuntap/BTap.c \
+ lwip/src/core/udp.c \
+ lwip/src/core/memp.c \
+ lwip/src/core/init.c \
+ lwip/src/core/pbuf.c \
+ lwip/src/core/tcp.c \
+ lwip/src/core/tcp_out.c \
+ lwip/src/core/netif.c \
+ lwip/src/core/def.c \
+ lwip/src/core/ip.c \
+ lwip/src/core/mem.c \
+ lwip/src/core/tcp_in.c \
+ lwip/src/core/stats.c \
+ lwip/src/core/inet_chksum.c \
+ lwip/src/core/timeouts.c \
+ lwip/src/core/ipv4/icmp.c \
+ lwip/src/core/ipv4/igmp.c \
+ lwip/src/core/ipv4/ip4_addr.c \
+ lwip/src/core/ipv4/ip4_frag.c \
+ lwip/src/core/ipv4/ip4.c \
+ lwip/src/core/ipv4/autoip.c \
+ lwip/src/core/ipv6/ethip6.c \
+ lwip/src/core/ipv6/inet6.c \
+ lwip/src/core/ipv6/ip6_addr.c \
+ lwip/src/core/ipv6/mld6.c \
+ lwip/src/core/ipv6/dhcp6.c \
+ lwip/src/core/ipv6/icmp6.c \
+ lwip/src/core/ipv6/ip6.c \
+ lwip/src/core/ipv6/ip6_frag.c \
+ lwip/src/core/ipv6/nd6.c \
+ lwip/custom/sys.c \
+ tun2socks/tun2socks.c \
+ base/DebugObject.c \
+ base/BLog.c \
+ base/BPending.c \
+ flowextra/PacketPassInactivityMonitor.c \
+ tun2socks/SocksUdpGwClient.c \
+ udpgw_client/UdpGwClient.c \
+ socks_udp_client/SocksUdpClient.c
+LOCAL_MODULE := tun2socks
+LOCAL_LDLIBS := -ldl -llog
+LOCAL_SRC_FILES := $(addprefix badvpn/, $(TUN2SOCKS_SOURCES))
+LOCAL_BUILD_SCRIPT := BUILD_EXECUTABLE
+LOCAL_MAKEFILE := $(local-makefile)
+$(call check-defined-LOCAL_MODULE,$(LOCAL_BUILD_SCRIPT))
+$(call check-LOCAL_MODULE,$(LOCAL_MAKEFILE))
+$(call check-LOCAL_MODULE_FILENAME)
+# we are building target objects
+my := TARGET_
+$(call handle-module-filename,lib,$(TARGET_SONAME_EXTENSION))
+$(call handle-module-built)
+LOCAL_MODULE_CLASS := EXECUTABLE
+include $(BUILD_SYSTEM)/build-module.mk
From 0a1695e3d79a475e7afcd9cd423c231baa7f0b06 Mon Sep 17 00:00:00 2001
From: alphax-hue3682 <191818854+alphax-hue3682@users.noreply.github.com>
Date: Fri, 7 Feb 2025 10:01:45 +0330
Subject: [PATCH 027/238] Update kotlin version to 2.1.10 (#4305)
* Update libs.versions.toml
* Update README.md
---
README.md | 2 +-
V2rayNG/gradle/libs.versions.toml | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 1bcc2499..c23ee0cf 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
A V2Ray client for Android, support [Xray core](https://github.com/XTLS/Xray-core) and [v2fly core](https://github.com/v2fly/v2ray-core)
[](https://developer.android.com/about/versions/lollipop)
-[](https://kotlinlang.org)
+[](https://kotlinlang.org)
[](https://github.com/2dust/v2rayNG/commits/master)
[](https://www.codefactor.io/repository/github/2dust/v2rayng)
[](https://github.com/2dust/v2rayNG/releases)
diff --git a/V2rayNG/gradle/libs.versions.toml b/V2rayNG/gradle/libs.versions.toml
index 43e11018..5dfb99cd 100644
--- a/V2rayNG/gradle/libs.versions.toml
+++ b/V2rayNG/gradle/libs.versions.toml
@@ -2,7 +2,7 @@
agp = "8.8.0"
desugar_jdk_libs = "2.1.4"
gradleLicensePlugin = "0.9.8"
-kotlin = "2.1.0"
+kotlin = "2.1.10"
coreKtx = "1.15.0"
junit = "4.13.2"
junitVersion = "1.2.1"
From 7d8a9f2b6d85921c80476558123b58190e67d194 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Sun, 9 Feb 2025 10:33:41 +0800
Subject: [PATCH 028/238] Update AndroidLibXrayLite
---
AndroidLibXrayLite | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/AndroidLibXrayLite b/AndroidLibXrayLite
index 78e06f32..e738d442 160000
--- a/AndroidLibXrayLite
+++ b/AndroidLibXrayLite
@@ -1 +1 @@
-Subproject commit 78e06f32b4a88a4e8cb7364abd4a1f8389a1da8b
+Subproject commit e738d44233204dbbef5107e126e1d744d67e65af
From ddf5f220377254d1fa624d4191ac0add99fc185b Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Sun, 9 Feb 2025 10:41:09 +0800
Subject: [PATCH 029/238] up 1.9.35
---
V2rayNG/app/build.gradle.kts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/V2rayNG/app/build.gradle.kts b/V2rayNG/app/build.gradle.kts
index 58dab903..0f783ef9 100644
--- a/V2rayNG/app/build.gradle.kts
+++ b/V2rayNG/app/build.gradle.kts
@@ -12,8 +12,8 @@ android {
applicationId = "com.v2ray.ang"
minSdk = 21
targetSdk = 35
- versionCode = 630
- versionName = "1.9.34"
+ versionCode = 631
+ versionName = "1.9.35"
multiDexEnabled = true
val abiFilterList = (properties["ABI_FILTERS"] as? String)?.split(';')
From d166b036fc8aa49ef4943d35f1f071e17e5093aa Mon Sep 17 00:00:00 2001
From: Hossin Asaadi
Date: Mon, 10 Feb 2025 21:31:22 -0500
Subject: [PATCH 030/238] Update ServerActivity.kt (#4326)
---
V2rayNG/app/src/main/java/com/v2ray/ang/ui/ServerActivity.kt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ServerActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ServerActivity.kt
index 9d0a6b0e..5600852c 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ServerActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ServerActivity.kt
@@ -356,11 +356,11 @@ class ServerActivity : BaseActivity() {
et_sni?.text = Utils.getEditable(config.sni)
config.fingerPrint?.let {
val utlsIndex = Utils.arrayFind(uTlsItems, it)
- sp_stream_fingerprint?.setSelection(utlsIndex)
+ utlsIndex.let { sp_stream_fingerprint?.setSelection(if (it >= 0) it else 0) }
}
config.alpn?.let {
val alpnIndex = Utils.arrayFind(alpns, it)
- sp_stream_alpn?.setSelection(alpnIndex)
+ alpnIndex.let { sp_stream_alpn?.setSelection(if (it >= 0) it else 0) }
}
if (config.security == TLS) {
container_allow_insecure?.visibility = View.VISIBLE
From b65e4b381904fc077845f4b27afb11541f12a5dd Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Tue, 11 Feb 2025 10:46:01 +0800
Subject: [PATCH 031/238] Bug fix
https://github.com/2dust/v2rayNG/issues/4329
---
.../com/v2ray/ang/ui/UrlSchemeActivity.kt | 19 ++++++++++++++-----
1 file changed, 14 insertions(+), 5 deletions(-)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/UrlSchemeActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/UrlSchemeActivity.kt
index fe16b2c5..2a287d11 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/UrlSchemeActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/UrlSchemeActivity.kt
@@ -4,10 +4,15 @@ import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.util.Log
+import androidx.lifecycle.lifecycleScope
import com.v2ray.ang.R
import com.v2ray.ang.databinding.ActivityLogcatBinding
import com.v2ray.ang.extension.toast
import com.v2ray.ang.handler.AngConfigManager
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
import java.net.URLDecoder
class UrlSchemeActivity : BaseActivity() {
@@ -66,11 +71,15 @@ class UrlSchemeActivity : BaseActivity() {
decodedUrl += "#${fragment}"
}
Log.d("UrlScheme-decodedUrl", decodedUrl)
- val (count, countSub) = AngConfigManager.importBatchConfig(decodedUrl, "", false)
- if (count + countSub > 0) {
- toast(R.string.import_subscription_success)
- } else {
- toast(R.string.import_subscription_failure)
+ lifecycleScope.launch(Dispatchers.IO) {
+ val (count, countSub) = AngConfigManager.importBatchConfig(decodedUrl, "", false)
+ withContext(Dispatchers.Main) {
+ if (count + countSub > 0) {
+ toast(R.string.import_subscription_success)
+ } else {
+ toast(R.string.import_subscription_failure)
+ }
+ }
}
}
}
From f497e4e3018510c64d4ac3d91c427981feefb60d Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Wed, 19 Feb 2025 18:13:34 +0800
Subject: [PATCH 032/238] Update AndroidLibXrayLite
---
AndroidLibXrayLite | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/AndroidLibXrayLite b/AndroidLibXrayLite
index e738d442..ec529cee 160000
--- a/AndroidLibXrayLite
+++ b/AndroidLibXrayLite
@@ -1 +1 @@
-Subproject commit e738d44233204dbbef5107e126e1d744d67e65af
+Subproject commit ec529ceeef91e7d5f493e20efb88fba8a39625d5
From e46b3546430bd018aa84cb0a6aee9d0dbaf93897 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Wed, 19 Feb 2025 18:18:43 +0800
Subject: [PATCH 033/238] up 1.9.36
---
V2rayNG/app/build.gradle.kts | 4 ++--
V2rayNG/gradle/libs.versions.toml | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/V2rayNG/app/build.gradle.kts b/V2rayNG/app/build.gradle.kts
index 0f783ef9..02b6bbce 100644
--- a/V2rayNG/app/build.gradle.kts
+++ b/V2rayNG/app/build.gradle.kts
@@ -12,8 +12,8 @@ android {
applicationId = "com.v2ray.ang"
minSdk = 21
targetSdk = 35
- versionCode = 631
- versionName = "1.9.35"
+ versionCode = 632
+ versionName = "1.9.36"
multiDexEnabled = true
val abiFilterList = (properties["ABI_FILTERS"] as? String)?.split(';')
diff --git a/V2rayNG/gradle/libs.versions.toml b/V2rayNG/gradle/libs.versions.toml
index 5dfb99cd..2a2de8f9 100644
--- a/V2rayNG/gradle/libs.versions.toml
+++ b/V2rayNG/gradle/libs.versions.toml
@@ -1,5 +1,5 @@
[versions]
-agp = "8.8.0"
+agp = "8.8.1"
desugar_jdk_libs = "2.1.4"
gradleLicensePlugin = "0.9.8"
kotlin = "2.1.10"
From dcfcf83430a016e4580d12f8a320ad5a1d68fb54 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Sat, 22 Feb 2025 14:52:05 +0800
Subject: [PATCH 034/238] Update AndroidLibXrayLite
---
AndroidLibXrayLite | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/AndroidLibXrayLite b/AndroidLibXrayLite
index ec529cee..5644ebf9 160000
--- a/AndroidLibXrayLite
+++ b/AndroidLibXrayLite
@@ -1 +1 @@
-Subproject commit ec529ceeef91e7d5f493e20efb88fba8a39625d5
+Subproject commit 5644ebf958c678d97eea1b6fa9e092dc18680886
From 40b3f0fedc42a089b2a3a12360cf3e6663d763ab Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Sat, 22 Feb 2025 15:17:29 +0800
Subject: [PATCH 035/238] up 1.9.37
---
V2rayNG/app/build.gradle.kts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/V2rayNG/app/build.gradle.kts b/V2rayNG/app/build.gradle.kts
index 02b6bbce..c29c4d8d 100644
--- a/V2rayNG/app/build.gradle.kts
+++ b/V2rayNG/app/build.gradle.kts
@@ -12,8 +12,8 @@ android {
applicationId = "com.v2ray.ang"
minSdk = 21
targetSdk = 35
- versionCode = 632
- versionName = "1.9.36"
+ versionCode = 633
+ versionName = "1.9.37"
multiDexEnabled = true
val abiFilterList = (properties["ABI_FILTERS"] as? String)?.split(';')
From 54c76d996807e284ac330adf8472753ca7e81256 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Sat, 22 Feb 2025 16:51:24 +0800
Subject: [PATCH 036/238] git submodule update --remote
---
badvpn | 2 +-
hysteria | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/badvpn b/badvpn
index 3cb49ab8..e6848008 160000
--- a/badvpn
+++ b/badvpn
@@ -1 +1 @@
-Subproject commit 3cb49ab81072dc84a27011937659d31467d18a0a
+Subproject commit e68480088a48c8930d29f54e30e9d6deaaf32c47
diff --git a/hysteria b/hysteria
index 8c052175..401ed524 160000
--- a/hysteria
+++ b/hysteria
@@ -1 +1 @@
-Subproject commit 8c0521759079884530ad24a4b9056c00fe93d371
+Subproject commit 401ed5245d9bdfe0a35d629a8d977f256da07a75
From 7c9fcd9f4353b4bf9e7057d1e2b0a396331463df Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Sat, 22 Feb 2025 17:08:23 +0800
Subject: [PATCH 037/238] Update build.gradle.kts
---
V2rayNG/app/build.gradle.kts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/V2rayNG/app/build.gradle.kts b/V2rayNG/app/build.gradle.kts
index c29c4d8d..0e88941a 100644
--- a/V2rayNG/app/build.gradle.kts
+++ b/V2rayNG/app/build.gradle.kts
@@ -12,7 +12,7 @@ android {
applicationId = "com.v2ray.ang"
minSdk = 21
targetSdk = 35
- versionCode = 633
+ versionCode = 634
versionName = "1.9.37"
multiDexEnabled = true
From 98fb0c433efa58c9612764141e0eedd7f93d52cd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E1=A1=A0=E1=A0=B5=E1=A1=A0=E1=A1=B3=20=E1=A1=A0=E1=A0=B5?=
=?UTF-8?q?=E1=A1=A0=20=E1=A0=AE=E1=A0=A0=E1=A0=A8=E1=A1=A9=E1=A0=8B?=
=?UTF-8?q?=E1=A0=A0=E1=A0=A8?=
<125150101+UjuiUjuMandan@users.noreply.github.com>
Date: Tue, 25 Feb 2025 09:31:11 +0800
Subject: [PATCH 038/238] fixup! Fix badvpn (#4302) (#4352)
thx
---
.github/workflows/build.yml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 999b84a2..66dabd71 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -25,8 +25,8 @@ jobs:
id: cache-libtun2socks-restore
uses: actions/cache/restore@v4
with:
- path: ${{ github.workspace }}/AndroidLibXrayLite/libs
- key: libtun2socks-${{ runner.os }}-${{ hashFiles('.git/modules/badvpn/refs/heads/main') }}-${{ hashFiles('.git/modules/libancillary/refs/heads/shadowsocks-android') }}
+ path: ${{ github.workspace }}/libs
+ key: libtun2socks-${{ runner.os }}-${{ hashFiles('.git/modules/badvpn/HEAD') }}-${{ hashFiles('.git/modules/libancillary/HEAD') }}
- name: Setup Android NDK
uses: nttld/setup-ndk@v1
@@ -61,7 +61,7 @@ jobs:
uses: actions/cache/save@v4
with:
path: ${{ github.workspace }}/libs
- key: libtun2socks-${{ runner.os }}-${{ hashFiles('.git/modules/badvpn/refs/heads/main') }}-${{ hashFiles('.git/modules/libancillary/refs/heads/shadowsocks-android') }}
+ key: libtun2socks-${{ runner.os }}-${{ hashFiles('.git/modules/badvpn/HEAD') }}-${{ hashFiles('.git/modules/libancillary/HEAD') }}
- name: Copy libtun2socks
run: |
From 9743d7b87b2c42d30efffb6fa3588894ddbc189d Mon Sep 17 00:00:00 2001
From: hhhkkmk <201595863+hhhkkmk@users.noreply.github.com>
Date: Tue, 4 Mar 2025 10:05:52 +0800
Subject: [PATCH 039/238] update (#4365)
---
V2rayNG/app/build.gradle.kts | 5 +-
.../v2ray/ang/service/V2RayServiceManager.kt | 26 +--
.../java/com/v2ray/ang/ui/AboutActivity.kt | 72 +++++---
.../java/com/v2ray/ang/ui/MainActivity.kt | 164 ++++++++++--------
.../com/v2ray/ang/ui/MainRecyclerAdapter.kt | 18 +-
.../com/v2ray/ang/ui/PerAppProxyActivity.kt | 137 +++------------
.../v2ray/ang/ui/RoutingSettingActivity.kt | 28 ++-
.../com/v2ray/ang/ui/ScScannerActivity.kt | 26 +--
.../java/com/v2ray/ang/ui/ScannerActivity.kt | 74 ++++----
.../com/v2ray/ang/ui/UserAssetActivity.kt | 66 +++----
.../java/com/v2ray/ang/util/AppManagerUtil.kt | 45 ++---
V2rayNG/gradle/libs.versions.toml | 12 +-
12 files changed, 318 insertions(+), 355 deletions(-)
diff --git a/V2rayNG/app/build.gradle.kts b/V2rayNG/app/build.gradle.kts
index 0e88941a..02ea3fad 100644
--- a/V2rayNG/app/build.gradle.kts
+++ b/V2rayNG/app/build.gradle.kts
@@ -157,9 +157,8 @@ dependencies {
implementation(libs.gson)
// Reactive and Utility Libraries
- implementation(libs.rxjava)
- implementation(libs.rxandroid)
- implementation(libs.rxpermissions)
+ implementation(libs.kotlinx.coroutines.android)
+ implementation(libs.kotlinx.coroutines.core)
// Language and Processing Libraries
implementation(libs.language.base)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt
index b94c7a10..0de09c2f 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt
@@ -31,10 +31,11 @@ import com.v2ray.ang.util.MessageUtil
import com.v2ray.ang.util.PluginUtil
import com.v2ray.ang.util.Utils
import go.Seq
-import io.reactivex.rxjava3.core.Observable
-import io.reactivex.rxjava3.disposables.Disposable
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import libv2ray.Libv2ray
import libv2ray.V2RayPoint
@@ -62,7 +63,7 @@ object V2RayServiceManager {
private var lastQueryTime = 0L
private var mBuilder: NotificationCompat.Builder? = null
- private var mDisposable: Disposable? = null
+ private var speedNotificationJob: Job? = null
private var mNotificationManager: NotificationManager? = null
fun startV2Ray(context: Context) {
@@ -365,8 +366,8 @@ object V2RayServiceManager {
}
mBuilder = null
- mDisposable?.dispose()
- mDisposable = null
+ speedNotificationJob?.cancel()
+ speedNotificationJob = null
}
private fun updateNotification(contentText: String?, proxyTraffic: Long, directTraffic: Long) {
@@ -393,7 +394,7 @@ object V2RayServiceManager {
}
private fun startSpeedNotification() {
- if (mDisposable == null &&
+ if (speedNotificationJob == null &&
v2rayPoint.isRunning &&
MmkvManager.decodeSettingsBool(AppConfig.PREF_SPEED_ENABLED) == true
) {
@@ -401,8 +402,8 @@ object V2RayServiceManager {
val outboundTags = currentConfig?.getAllOutboundTags()
outboundTags?.remove(TAG_DIRECT)
- mDisposable = Observable.interval(3, java.util.concurrent.TimeUnit.SECONDS)
- .subscribe {
+ speedNotificationJob = CoroutineScope(Dispatchers.IO).launch {
+ while (isActive) {
val queryTime = System.currentTimeMillis()
val sinceLastQueryInSeconds = (queryTime - lastQueryTime) / 1000.0
var proxyTotal = 0L
@@ -430,7 +431,9 @@ object V2RayServiceManager {
}
lastZeroSpeed = zeroSpeed
lastQueryTime = queryTime
+ delay(3000)
}
+ }
}
}
@@ -445,11 +448,10 @@ object V2RayServiceManager {
}
private fun stopSpeedNotification() {
- mDisposable?.let {
- it.dispose() //stop queryStats
- mDisposable = null
+ speedNotificationJob?.let {
+ it.cancel()
+ speedNotificationJob = null
updateNotification(currentConfig?.remarks, 0, 0)
}
}
-
}
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/AboutActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/AboutActivity.kt
index eacacb35..806758af 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/AboutActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/AboutActivity.kt
@@ -5,15 +5,16 @@ import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.util.Log
+import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
+import androidx.core.app.ActivityCompat
+import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
-import com.tbruyelle.rxpermissions3.RxPermissions
import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig
import com.v2ray.ang.BuildConfig
import com.v2ray.ang.R
import com.v2ray.ang.databinding.ActivityAboutBinding
-import com.v2ray.ang.extension.toast
import com.v2ray.ang.util.SpeedtestUtil
import com.v2ray.ang.util.Utils
import com.v2ray.ang.util.ZipUtil
@@ -21,11 +22,24 @@ import java.io.File
import java.text.SimpleDateFormat
import java.util.Locale
-
class AboutActivity : BaseActivity() {
+
private val binding by lazy { ActivityAboutBinding.inflate(layoutInflater) }
private val extDir by lazy { File(Utils.backupPath(this)) }
+ private val requestPermissionLauncher =
+ registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
+ if (isGranted) {
+ try {
+ showFileChooser()
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ } else {
+ toast(R.string.toast_permission_denied)
+ }
+ }
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
@@ -33,6 +47,7 @@ class AboutActivity : BaseActivity() {
title = getString(R.string.title_about)
binding.tvBackupSummary.text = this.getString(R.string.summary_configuration_backup, extDir)
+
binding.layoutBackup.setOnClickListener {
val ret = backupConfiguration(extDir.absolutePath)
if (ret.first) {
@@ -50,7 +65,8 @@ class AboutActivity : BaseActivity() {
Intent(Intent.ACTION_SEND).setType("application/zip")
.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
.putExtra(
- Intent.EXTRA_STREAM, FileProvider.getUriForFile(
+ Intent.EXTRA_STREAM,
+ FileProvider.getUriForFile(
this, BuildConfig.APPLICATION_ID + ".cache", File(ret.second)
)
), getString(R.string.title_configuration_share)
@@ -62,23 +78,22 @@ class AboutActivity : BaseActivity() {
}
binding.layoutRestore.setOnClickListener {
- val permission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
- Manifest.permission.READ_MEDIA_IMAGES
- } else {
- Manifest.permission.READ_EXTERNAL_STORAGE
- }
- RxPermissions(this)
- .request(permission)
- .subscribe {
- if (it) {
- try {
- showFileChooser()
- } catch (e: Exception) {
- e.printStackTrace()
- }
- } else
- toast(R.string.toast_permission_denied)
+ val permission =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ Manifest.permission.READ_MEDIA_IMAGES
+ } else {
+ Manifest.permission.READ_EXTERNAL_STORAGE
}
+
+ if (ContextCompat.checkSelfPermission(this, permission) == android.content.pm.PackageManager.PERMISSION_GRANTED) {
+ try {
+ showFileChooser()
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ } else {
+ requestPermissionLauncher.launch(permission)
+ }
}
binding.layoutSoureCcode.setOnClickListener {
@@ -88,13 +103,15 @@ class AboutActivity : BaseActivity() {
binding.layoutFeedback.setOnClickListener {
Utils.openUri(this, AppConfig.v2rayNGIssues)
}
- binding.layoutOssLicenses.setOnClickListener{
+
+ binding.layoutOssLicenses.setOnClickListener {
val webView = android.webkit.WebView(this);
- webView.loadUrl("file:///android_asset/open_source_licenses.html");
+ webView.loadUrl("file:///android_asset/open_source_licenses.html")
android.app.AlertDialog.Builder(this)
.setTitle("Open source licenses")
.setView(webView)
- .setPositiveButton("OK", android.content.DialogInterface.OnClickListener { dialog, whichButton -> dialog.dismiss() }).show()
+ .setPositiveButton("OK") { dialog, _ -> dialog.dismiss() }
+ .show()
}
binding.layoutTgChannel.setOnClickListener {
@@ -157,9 +174,9 @@ class AboutActivity : BaseActivity() {
}
private val chooseFile =
- registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
- val uri = it.data?.data
- if (it.resultCode == RESULT_OK && uri != null) {
+ registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
+ val uri = result.data?.data
+ if (result.resultCode == RESULT_OK && uri != null) {
try {
val targetFile =
File(this.cacheDir.absolutePath, "${System.currentTimeMillis()}.zip")
@@ -180,4 +197,7 @@ class AboutActivity : BaseActivity() {
}
}
+ private fun toast(messageResId: Int) {
+ Toast.makeText(this, getString(messageResId), Toast.LENGTH_SHORT).show()
+ }
}
\ No newline at end of file
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt
index 53ae34ea..8b7b336c 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt
@@ -3,6 +3,7 @@ package com.v2ray.ang.ui
import android.Manifest
import android.content.ActivityNotFoundException
import android.content.Intent
+import android.content.pm.PackageManager
import android.content.res.ColorStateList
import android.net.Uri
import android.net.VpnService
@@ -27,7 +28,6 @@ import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.navigation.NavigationView
import com.google.android.material.tabs.TabLayout
-import com.tbruyelle.rxpermissions3.RxPermissions
import com.v2ray.ang.AppConfig
import com.v2ray.ang.AppConfig.VPN
import com.v2ray.ang.R
@@ -41,8 +41,6 @@ import com.v2ray.ang.helper.SimpleItemTouchHelperCallback
import com.v2ray.ang.service.V2RayServiceManager
import com.v2ray.ang.util.Utils
import com.v2ray.ang.viewmodel.MainViewModel
-import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
-import io.reactivex.rxjava3.core.Observable
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@@ -81,6 +79,60 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
private var mItemTouchHelper: ItemTouchHelper? = null
val mainViewModel: MainViewModel by viewModels()
+ // register activity result for requesting permission
+ private val requestPermissionLauncher =
+ registerForActivityResult(
+ ActivityResultContracts.RequestPermission()
+ ) { isGranted: Boolean ->
+ if (isGranted) {
+ when (pendingAction) {
+ Action.IMPORT_QR_CODE_CONFIG ->
+ scanQRCodeForConfig.launch(Intent(this, ScannerActivity::class.java))
+ Action.IMPORT_QR_CODE_URL ->
+ scanQRCodeForUrlToCustomConfig.launch(Intent(this, ScannerActivity::class.java))
+ Action.READ_CONTENT_FROM_URI ->
+ chooseFileForCustomConfig.launch(Intent.createChooser(Intent(Intent.ACTION_GET_CONTENT).apply {
+ type = "*/*"
+ addCategory(Intent.CATEGORY_OPENABLE)
+ }, getString(R.string.title_file_chooser)))
+ Action.POST_NOTIFICATIONS -> {}
+ else -> {}
+ }
+ } else {
+ toast(R.string.toast_permission_denied)
+ }
+ pendingAction = Action.NONE
+ }
+
+ private var pendingAction: Action = Action.NONE
+
+ enum class Action {
+ NONE,
+ IMPORT_QR_CODE_CONFIG,
+ IMPORT_QR_CODE_URL,
+ READ_CONTENT_FROM_URI,
+ POST_NOTIFICATIONS
+ }
+
+ private val chooseFileForCustomConfig = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
+ val uri = it.data?.data
+ if (it.resultCode == RESULT_OK && uri != null) {
+ readContentFromUri(uri)
+ }
+ }
+
+ private val scanQRCodeForConfig = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
+ if (it.resultCode == RESULT_OK) {
+ importBatchConfig(it.data?.getStringExtra("SCAN_RESULT"))
+ }
+ }
+
+ private val scanQRCodeForUrlToCustomConfig = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
+ if (it.resultCode == RESULT_OK) {
+ importConfigCustomUrl(it.data?.getStringExtra("SCAN_RESULT"))
+ }
+ }
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
@@ -129,12 +181,10 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
migrateLegacy()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
- RxPermissions(this)
- .request(Manifest.permission.POST_NOTIFICATIONS)
- .subscribe {
- if (!it)
- toast(R.string.toast_permission_denied_notification)
- }
+ if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
+ pendingAction = Action.POST_NOTIFICATIONS
+ requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
+ }
}
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
@@ -226,11 +276,10 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
if (mainViewModel.isRunning.value == true) {
Utils.stopVService(this)
}
- Observable.timer(500, TimeUnit.MILLISECONDS)
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe {
- startV2Ray()
- }
+ lifecycleScope.launch {
+ delay(500)
+ startV2Ray()
+ }
}
public override fun onResume() {
@@ -462,38 +511,20 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
* import config from qrcode
*/
private fun importQRcode(forConfig: Boolean): Boolean {
-// try {
-// startActivityForResult(Intent("com.google.zxing.client.android.SCAN")
-// .addCategory(Intent.CATEGORY_DEFAULT)
-// .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP), requestCode)
-// } catch (e: Exception) {
- RxPermissions(this)
- .request(Manifest.permission.CAMERA)
- .subscribe {
- if (it)
- if (forConfig)
- scanQRCodeForConfig.launch(Intent(this, ScannerActivity::class.java))
- else
- scanQRCodeForUrlToCustomConfig.launch(Intent(this, ScannerActivity::class.java))
- else
- toast(R.string.toast_permission_denied)
+ val permission = Manifest.permission.CAMERA
+ if (ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED) {
+ if (forConfig) {
+ scanQRCodeForConfig.launch(Intent(this, ScannerActivity::class.java))
+ } else {
+ scanQRCodeForUrlToCustomConfig.launch(Intent(this, ScannerActivity::class.java))
}
-// }
+ } else {
+ pendingAction = if (forConfig) Action.IMPORT_QR_CODE_CONFIG else Action.IMPORT_QR_CODE_URL
+ requestPermissionLauncher.launch(permission)
+ }
return true
}
- private val scanQRCodeForConfig = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
- if (it.resultCode == RESULT_OK) {
- importBatchConfig(it.data?.getStringExtra("SCAN_RESULT"))
- }
- }
-
- private val scanQRCodeForUrlToCustomConfig = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
- if (it.resultCode == RESULT_OK) {
- importConfigCustomUrl(it.data?.getStringExtra("SCAN_RESULT"))
- }
- }
-
/**
* import config from clipboard
*/
@@ -614,10 +645,6 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
* import config from sub
*/
private fun importConfigViaSub(): Boolean {
-// val dialog = AlertDialog.Builder(this)
-// .setView(LayoutProgressBinding.inflate(layoutInflater).root)
-// .setCancelable(false)
-// .show()
binding.pbWaiting.show()
lifecycleScope.launch(Dispatchers.IO) {
@@ -630,7 +657,6 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
} else {
toast(R.string.toast_failure)
}
- //dialog.dismiss()
binding.pbWaiting.hide()
}
}
@@ -645,17 +671,17 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
intent.type = "*/*"
intent.addCategory(Intent.CATEGORY_OPENABLE)
- try {
- chooseFileForCustomConfig.launch(Intent.createChooser(intent, getString(R.string.title_file_chooser)))
- } catch (ex: ActivityNotFoundException) {
- toast(R.string.toast_require_file_manager)
+ val permission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ Manifest.permission.READ_MEDIA_IMAGES
+ } else {
+ Manifest.permission.READ_EXTERNAL_STORAGE
}
- }
- private val chooseFileForCustomConfig = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
- val uri = it.data?.data
- if (it.resultCode == RESULT_OK && uri != null) {
- readContentFromUri(uri)
+ if (ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED) {
+ pendingAction = Action.READ_CONTENT_FROM_URI
+ chooseFileForCustomConfig.launch(Intent.createChooser(intent, getString(R.string.title_file_chooser)))
+ } else {
+ requestPermissionLauncher.launch(permission)
}
}
@@ -668,20 +694,18 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
} else {
Manifest.permission.READ_EXTERNAL_STORAGE
}
- RxPermissions(this)
- .request(permission)
- .subscribe {
- if (it) {
- try {
- contentResolver.openInputStream(uri).use { input ->
- importCustomizeConfig(input?.bufferedReader()?.readText())
- }
- } catch (e: Exception) {
- e.printStackTrace()
- }
- } else
- toast(R.string.toast_permission_denied)
+
+ if (ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED) {
+ try {
+ contentResolver.openInputStream(uri).use { input ->
+ importCustomizeConfig(input?.bufferedReader()?.readText())
+ }
+ } catch (e: Exception) {
+ e.printStackTrace()
}
+ } else {
+ requestPermissionLauncher.launch(permission)
+ }
}
/**
@@ -763,4 +787,4 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
binding.drawerLayout.closeDrawer(GravityCompat.START)
return true
}
-}
+}
\ No newline at end of file
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainRecyclerAdapter.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainRecyclerAdapter.kt
index 30bc3ed2..9801cd90 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainRecyclerAdapter.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainRecyclerAdapter.kt
@@ -8,6 +8,7 @@ import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat
+import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.RecyclerView
import com.v2ray.ang.AngApplication.Companion.application
import com.v2ray.ang.AppConfig
@@ -23,9 +24,9 @@ import com.v2ray.ang.helper.ItemTouchHelperAdapter
import com.v2ray.ang.helper.ItemTouchHelperViewHolder
import com.v2ray.ang.service.V2RayServiceManager
import com.v2ray.ang.util.Utils
-import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
-import io.reactivex.rxjava3.core.Observable
-import java.util.concurrent.TimeUnit
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import java.util.*
class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter(), ItemTouchHelperAdapter {
companion object {
@@ -165,11 +166,14 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter
- if (blacklist.contains(one.packageName)) {
- one.isSelected = 1
- } else {
- one.isSelected = 0
+ lifecycleScope.launch {
+ try {
+ binding.pbWaiting.visibility = View.VISIBLE
+ val blacklist = MmkvManager.decodeSettingsStringSet(AppConfig.PREF_PER_APP_PROXY_SET)
+ val apps = withContext(Dispatchers.IO) {
+ val appsList = AppManagerUtil.loadNetworkAppList(this@PerAppProxyActivity)
+
+ if (blacklist != null) {
+ appsList.forEach { app ->
+ app.isSelected = if (blacklist.contains(app.packageName)) 1 else 0
}
- }
- val comparator = Comparator { p1, p2 ->
- when {
- p1.isSelected > p2.isSelected -> -1
- p1.isSelected == p2.isSelected -> 0
- else -> 1
- }
- }
- it.sortedWith(comparator)
- } else {
- val comparator = object : Comparator {
+ appsList.sortedWith(Comparator { p1, p2 ->
+ when {
+ p1.isSelected > p2.isSelected -> -1
+ p1.isSelected == p2.isSelected -> 0
+ else -> 1
+ }
+ })
+ } else {
val collator = Collator.getInstance()
- override fun compare(o1: AppInfo, o2: AppInfo) = collator.compare(o1.appName, o2.appName)
+ appsList.sortedWith(compareBy(collator) { it.appName })
}
- it.sortedWith(comparator)
}
- }
-// .map {
-// val comparator = object : Comparator {
-// val collator = Collator.getInstance()
-// override fun compare(o1: AppInfo, o2: AppInfo) = collator.compare(o1.appName, o2.appName)
-// }
-// it.sortedWith(comparator)
-// }
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe {
- appsAll = it
- adapter = PerAppProxyAdapter(this, it, blacklist)
+
+ appsAll = apps
+ adapter = PerAppProxyAdapter(this@PerAppProxyActivity, apps, blacklist)
binding.recyclerView.adapter = adapter
binding.pbWaiting.visibility = View.GONE
+ } catch (e: Exception) {
+ binding.pbWaiting.visibility = View.GONE
+ Log.e(ANG_PACKAGE, "Error loading apps", e)
}
- /***
- recycler_view.addOnScrollListener(object : RecyclerView.OnScrollListener() {
- var dst = 0
- val threshold = resources.getDimensionPixelSize(R.dimen.bypass_list_header_height) * 2
- override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
- dst += dy
- if (dst > threshold) {
- header_view.hide()
- dst = 0
- } else if (dst < -20) {
- header_view.show()
- dst = 0
}
- }
-
- var hiding = false
- fun View.hide() {
- val target = -height.toFloat()
- if (hiding || translationY == target) return
- animate()
- .translationY(target)
- .setInterpolator(AccelerateInterpolator(2F))
- .setListener(object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator?) {
- hiding = false
- }
- })
- hiding = true
- }
-
- var showing = false
- fun View.show() {
- val target = 0f
- if (showing || translationY == target) return
- animate()
- .translationY(target)
- .setInterpolator(DecelerateInterpolator(2F))
- .setListener(object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator?) {
- showing = false
- }
- })
- showing = true
- }
- })
- ***/
binding.switchPerAppProxy.setOnCheckedChangeListener { _, isChecked ->
MmkvManager.encodeSettings(AppConfig.PREF_PER_APP_PROXY, isChecked)
@@ -140,36 +85,6 @@ class PerAppProxyActivity : BaseActivity() {
MmkvManager.encodeSettings(AppConfig.PREF_BYPASS_APPS, isChecked)
}
binding.switchBypassApps.isChecked = MmkvManager.decodeSettingsBool(AppConfig.PREF_BYPASS_APPS, false)
-
- /***
- et_search.setOnEditorActionListener { v, actionId, event ->
- if (actionId == EditorInfo.IME_ACTION_SEARCH) {
- //hide
- var imm: InputMethodManager = v.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
- imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS)
-
- val key = v.text.toString().toUpperCase()
- val apps = ArrayList()
- if (TextUtils.isEmpty(key)) {
- appsAll?.forEach {
- apps.add(it)
- }
- } else {
- appsAll?.forEach {
- if (it.appName.toUpperCase().indexOf(key) >= 0) {
- apps.add(it)
- }
- }
- }
- adapter = PerAppProxyAdapter(this, apps, adapter?.blacklist)
- recycler_view.adapter = adapter
- adapter?.notifyDataSetChanged()
- true
- } else {
- false
- }
- }
- ***/
}
override fun onPause() {
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/RoutingSettingActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/RoutingSettingActivity.kt
index 44410d67..5681f75d 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/RoutingSettingActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/RoutingSettingActivity.kt
@@ -12,7 +12,6 @@ import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
-import com.tbruyelle.rxpermissions3.RxPermissions
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
import com.v2ray.ang.databinding.ActivityRoutingSettingBinding
@@ -39,6 +38,16 @@ class RoutingSettingActivity : BaseActivity() {
private val preset_rulesets: Array by lazy {
resources.getStringArray(R.array.preset_rulesets)
}
+
+ private val requestCameraPermissionLauncher = registerForActivityResult(
+ ActivityResultContracts.RequestPermission()
+ ) { isGranted: Boolean ->
+ if (isGranted) {
+ scanQRcodeForRulesets.launch(Intent(this, ScannerActivity::class.java))
+ } else {
+ toast(R.string.toast_permission_denied)
+ }
+ }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -102,11 +111,9 @@ class RoutingSettingActivity : BaseActivity() {
e.printStackTrace()
}
}.show()
-
-
}
.setNegativeButton(android.R.string.cancel) { _, _ ->
- //do noting
+ //do nothing
}
.show()
true
@@ -142,18 +149,10 @@ class RoutingSettingActivity : BaseActivity() {
}
R.id.import_rulesets_from_qrcode -> {
- RxPermissions(this)
- .request(Manifest.permission.CAMERA)
- .subscribe {
- if (it)
- scanQRcodeForRulesets.launch(Intent(this, ScannerActivity::class.java))
- else
- toast(R.string.toast_permission_denied)
- }
+ requestCameraPermissionLauncher.launch(Manifest.permission.CAMERA)
true
}
-
R.id.export_rulesets_to_clipboard -> {
val rulesetList = MmkvManager.decodeRoutingRulesets()
if (rulesetList.isNullOrEmpty()) {
@@ -201,5 +200,4 @@ class RoutingSettingActivity : BaseActivity() {
rulesets.addAll(MmkvManager.decodeRoutingRulesets() ?: mutableListOf())
adapter.notifyDataSetChanged()
}
-
-}
+}
\ No newline at end of file
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ScScannerActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ScScannerActivity.kt
index 187a6de3..0d9a7d22 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ScScannerActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ScScannerActivity.kt
@@ -4,13 +4,23 @@ import android.Manifest
import android.content.Intent
import android.os.Bundle
import androidx.activity.result.contract.ActivityResultContracts
-import com.tbruyelle.rxpermissions3.RxPermissions
import com.v2ray.ang.R
import com.v2ray.ang.extension.toast
import com.v2ray.ang.handler.AngConfigManager
class ScScannerActivity : BaseActivity() {
+ private val requestCameraPermissionLauncher = registerForActivityResult(
+ ActivityResultContracts.RequestPermission()
+ ) { isGranted: Boolean ->
+ if (isGranted) {
+ scanQRCode.launch(Intent(this, ScannerActivity::class.java))
+ } else {
+ toast(R.string.toast_permission_denied)
+ finish()
+ }
+ }
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_none)
@@ -18,19 +28,10 @@ class ScScannerActivity : BaseActivity() {
}
fun importQRcode(): Boolean {
- RxPermissions(this)
- .request(Manifest.permission.CAMERA)
- .subscribe { granted ->
- if (granted) {
- scanQRCode.launch(Intent(this, ScannerActivity::class.java))
- } else {
- toast(R.string.toast_permission_denied)
- }
- }
+ requestCameraPermissionLauncher.launch(Manifest.permission.CAMERA)
return true
}
-
private val scanQRCode = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == RESULT_OK) {
val scanResult = it.data?.getStringExtra("SCAN_RESULT").orEmpty()
@@ -46,5 +47,4 @@ class ScScannerActivity : BaseActivity() {
}
finish()
}
-
-}
+}
\ No newline at end of file
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ScannerActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ScannerActivity.kt
index 6402c5a2..829d345d 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ScannerActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ScannerActivity.kt
@@ -2,13 +2,15 @@ package com.v2ray.ang.ui
import android.Manifest
import android.content.Intent
+import android.content.pm.PackageManager
import android.graphics.BitmapFactory
import android.os.Build
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import androidx.activity.result.contract.ActivityResultContracts
-import com.tbruyelle.rxpermissions3.RxPermissions
+import androidx.core.app.ActivityCompat
+import androidx.core.content.ContextCompat
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
import com.v2ray.ang.extension.toast
@@ -21,6 +23,37 @@ import io.github.g00fy2.quickie.config.ScannerConfig
class ScannerActivity : BaseActivity() {
private val scanQrCode = registerForActivityResult(ScanCustomCode(), ::handleResult)
+ private val chooseFile = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
+ val uri = it.data?.data
+ if (it.resultCode == RESULT_OK && uri != null) {
+ try {
+ val inputStream = contentResolver.openInputStream(uri)
+ val bitmap = BitmapFactory.decodeStream(inputStream)
+ inputStream?.close()
+
+ val text = QRCodeDecoder.syncDecodeQRCode(bitmap)
+ if (text.isNullOrEmpty()) {
+ toast(R.string.toast_decoding_failed)
+ } else {
+ finished(text)
+ }
+ } catch (e: Exception) {
+ e.printStackTrace()
+ toast(R.string.toast_decoding_failed)
+ }
+ }
+ }
+
+ private val requestPermissionLauncher =
+ registerForActivityResult(
+ ActivityResultContracts.RequestPermission()
+ ) { isGranted: Boolean ->
+ if (isGranted) {
+ showFileChooser()
+ } else {
+ toast(R.string.toast_permission_denied)
+ }
+ }
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -72,15 +105,12 @@ class ScannerActivity : BaseActivity() {
} else {
Manifest.permission.READ_EXTERNAL_STORAGE
}
- RxPermissions(this)
- .request(permission)
- .subscribe { granted ->
- if (granted) {
- showFileChooser()
- } else {
- toast(R.string.toast_permission_denied)
- }
- }
+
+ if (ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED) {
+ showFileChooser()
+ } else {
+ requestPermissionLauncher.launch(permission)
+ }
true
}
@@ -100,26 +130,4 @@ class ScannerActivity : BaseActivity() {
toast(R.string.toast_require_file_manager)
}
}
-
- private val chooseFile = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
- val uri = it.data?.data
- if (it.resultCode == RESULT_OK && uri != null) {
- try {
- val inputStream = contentResolver.openInputStream(uri)
- val bitmap = BitmapFactory.decodeStream(inputStream)
- inputStream?.close()
-
- val text = QRCodeDecoder.syncDecodeQRCode(bitmap)
- if (text.isNullOrEmpty()) {
- toast(R.string.toast_decoding_failed)
- } else {
- finished(text)
- }
- } catch (e: Exception) {
- e.printStackTrace()
- toast(R.string.toast_decoding_failed)
- }
- }
- }
-
-}
+}
\ No newline at end of file
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/UserAssetActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/UserAssetActivity.kt
index c59afe5b..06d371e6 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/UserAssetActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/UserAssetActivity.kt
@@ -19,7 +19,6 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
-import com.tbruyelle.rxpermissions3.RxPermissions
import com.v2ray.ang.AppConfig
import com.v2ray.ang.AppConfig.LOOPBACK
import com.v2ray.ang.R
@@ -50,6 +49,38 @@ class UserAssetActivity : BaseActivity() {
val extDir by lazy { File(Utils.userAssetPath(this)) }
val builtInGeoFiles = arrayOf("geosite.dat", "geoip.dat")
+ private val requestStoragePermissionLauncher = registerForActivityResult(
+ ActivityResultContracts.RequestPermission()
+ ) { isGranted: Boolean ->
+ if (isGranted) {
+ val intent = Intent(Intent.ACTION_GET_CONTENT)
+ intent.type = "*/*"
+ intent.addCategory(Intent.CATEGORY_OPENABLE)
+
+ try {
+ chooseFile.launch(
+ Intent.createChooser(
+ intent,
+ getString(R.string.title_file_chooser)
+ )
+ )
+ } catch (ex: android.content.ActivityNotFoundException) {
+ toast(R.string.toast_require_file_manager)
+ }
+ } else {
+ toast(R.string.toast_permission_denied)
+ }
+ }
+
+ private val requestCameraPermissionLauncher = registerForActivityResult(
+ ActivityResultContracts.RequestPermission()
+ ) { isGranted: Boolean ->
+ if (isGranted) {
+ scanQRCodeForAssetURL.launch(Intent(this, ScannerActivity::class.java))
+ } else {
+ toast(R.string.toast_permission_denied)
+ }
+ }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -86,27 +117,7 @@ class UserAssetActivity : BaseActivity() {
} else {
Manifest.permission.READ_EXTERNAL_STORAGE
}
- RxPermissions(this)
- .request(permission)
- .subscribe {
- if (it) {
- val intent = Intent(Intent.ACTION_GET_CONTENT)
- intent.type = "*/*"
- intent.addCategory(Intent.CATEGORY_OPENABLE)
-
- try {
- chooseFile.launch(
- Intent.createChooser(
- intent,
- getString(R.string.title_file_chooser)
- )
- )
- } catch (ex: android.content.ActivityNotFoundException) {
- toast(R.string.toast_require_file_manager)
- }
- } else
- toast(R.string.toast_permission_denied)
- }
+ requestStoragePermissionLauncher.launch(permission)
}
val chooseFile = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
@@ -158,14 +169,7 @@ class UserAssetActivity : BaseActivity() {
}
private fun importAssetFromQRcode(): Boolean {
- RxPermissions(this)
- .request(Manifest.permission.CAMERA)
- .subscribe {
- if (it)
- scanQRCodeForAssetURL.launch(Intent(this, ScannerActivity::class.java))
- else
- toast(R.string.toast_permission_denied)
- }
+ requestCameraPermissionLauncher.launch(Manifest.permission.CAMERA)
return true
}
@@ -349,4 +353,4 @@ class UserAssetActivity : BaseActivity() {
class UserAssetViewHolder(val itemUserAssetBinding: ItemRecyclerUserAssetBinding) :
RecyclerView.ViewHolder(itemUserAssetBinding.root)
-}
+}
\ No newline at end of file
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/util/AppManagerUtil.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/util/AppManagerUtil.kt
index 8ce94751..e2300512 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/util/AppManagerUtil.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/util/AppManagerUtil.kt
@@ -4,36 +4,27 @@ import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import com.v2ray.ang.dto.AppInfo
-import io.reactivex.rxjava3.core.Observable
+import kotlinx.coroutines.withContext
+import kotlinx.coroutines.Dispatchers
object AppManagerUtil {
- private fun loadNetworkAppList(ctx: Context): ArrayList {
- val packageManager = ctx.packageManager
- val packages = packageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS)
- val apps = ArrayList()
+ suspend fun loadNetworkAppList(context: Context): ArrayList =
+ withContext(Dispatchers.IO) {
+ val packageManager = context.packageManager
+ val packages = packageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS)
+ val apps = ArrayList()
- for (pkg in packages) {
- val applicationInfo = pkg.applicationInfo ?: continue
+ for (pkg in packages) {
+ val applicationInfo = pkg.applicationInfo ?: continue
- val appName = applicationInfo.loadLabel(packageManager).toString()
- val appIcon = applicationInfo.loadIcon(packageManager) ?: continue
- val isSystemApp = (applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM) > 0
+ val appName = applicationInfo.loadLabel(packageManager).toString()
+ val appIcon = applicationInfo.loadIcon(packageManager) ?: continue
+ val isSystemApp = (applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM) > 0
- val appInfo = AppInfo(appName, pkg.packageName, appIcon, isSystemApp, 0)
- apps.add(appInfo)
+ val appInfo = AppInfo(appName, pkg.packageName, appIcon, isSystemApp, 0)
+ apps.add(appInfo)
+ }
+
+ return@withContext apps
}
-
- return apps
- }
-
- fun rxLoadNetworkAppList(ctx: Context): Observable> =
- Observable.unsafeCreate {
- it.onNext(loadNetworkAppList(ctx))
- }
-
-// val PackageInfo.hasInternetPermission: Boolean
-// get() {
-// val permissions = requestedPermissions
-// return permissions?.any { it == Manifest.permission.INTERNET } ?: false
-// }
-}
+}
\ No newline at end of file
diff --git a/V2rayNG/gradle/libs.versions.toml b/V2rayNG/gradle/libs.versions.toml
index 2a2de8f9..974aa910 100644
--- a/V2rayNG/gradle/libs.versions.toml
+++ b/V2rayNG/gradle/libs.versions.toml
@@ -11,12 +11,11 @@ appcompat = "1.7.0"
material = "1.12.0"
activity = "1.10.0"
constraintlayout = "2.2.0"
-mmkvStatic = "1.3.11"
+mmkvStatic = "1.3.12"
gson = "2.11.0"
quickieFoss = "1.13.1"
-rxjava = "3.1.10"
-rxandroid = "3.0.2"
-rxpermissions = "0.12"
+kotlinx-coroutines-android = "1.10.1"
+kotlinx-coroutines-core = "1.10.1"
swiperefreshlayout = "1.1.0"
toastcompat = "1.1.0"
editorkit = "2.9.0"
@@ -43,9 +42,8 @@ androidx-constraintlayout = { group = "androidx.constraintlayout", name = "const
mmkv-static = { module = "com.tencent:mmkv-static", version.ref = "mmkvStatic" }
gson = { module = "com.google.code.gson:gson", version.ref = "gson" }
quickie-foss = { module = "com.github.T8RIN.QuickieExtended:quickie-foss", version.ref = "quickieFoss" }
-rxjava = { module = "io.reactivex.rxjava3:rxjava", version.ref = "rxjava" }
-rxandroid = { module = "io.reactivex.rxjava3:rxandroid", version.ref = "rxandroid" }
-rxpermissions = { module = "com.github.tbruyelle:rxpermissions", version.ref = "rxpermissions" }
+kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version = "kotlinx-coroutines-android" }
+kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version = "kotlinx-coroutines-core" }
toastcompat = { module = "me.drakeet.support:toastcompat", version.ref = "toastcompat" }
editorkit = { module = "com.blacksquircle.ui:editorkit", version.ref = "editorkit" }
language-base = { module = "com.blacksquircle.ui:language-base", version.ref = "editorkit" }
From aa328f0add237a9f450344f2eebc8de101482c0a Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Tue, 4 Mar 2025 10:12:46 +0800
Subject: [PATCH 040/238] Update AndroidLibXrayLite
---
AndroidLibXrayLite | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/AndroidLibXrayLite b/AndroidLibXrayLite
index 5644ebf9..36f046e2 160000
--- a/AndroidLibXrayLite
+++ b/AndroidLibXrayLite
@@ -1 +1 @@
-Subproject commit 5644ebf958c678d97eea1b6fa9e092dc18680886
+Subproject commit 36f046e27b2ffd95e9c0bc3d836bd4de7cdade9d
From fd9c5040bf6c2c1c7cf4ed0b882d4ea94c7b655d Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Tue, 4 Mar 2025 10:19:39 +0800
Subject: [PATCH 041/238] up 1.9.38
---
V2rayNG/app/build.gradle.kts | 4 ++--
V2rayNG/gradle/libs.versions.toml | 8 ++++----
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/V2rayNG/app/build.gradle.kts b/V2rayNG/app/build.gradle.kts
index 02ea3fad..92d51066 100644
--- a/V2rayNG/app/build.gradle.kts
+++ b/V2rayNG/app/build.gradle.kts
@@ -12,8 +12,8 @@ android {
applicationId = "com.v2ray.ang"
minSdk = 21
targetSdk = 35
- versionCode = 634
- versionName = "1.9.37"
+ versionCode = 635
+ versionName = "1.9.38"
multiDexEnabled = true
val abiFilterList = (properties["ABI_FILTERS"] as? String)?.split(';')
diff --git a/V2rayNG/gradle/libs.versions.toml b/V2rayNG/gradle/libs.versions.toml
index 974aa910..79a837df 100644
--- a/V2rayNG/gradle/libs.versions.toml
+++ b/V2rayNG/gradle/libs.versions.toml
@@ -1,6 +1,6 @@
[versions]
-agp = "8.8.1"
-desugar_jdk_libs = "2.1.4"
+agp = "8.8.2"
+desugar_jdk_libs = "2.1.5"
gradleLicensePlugin = "0.9.8"
kotlin = "2.1.10"
coreKtx = "1.15.0"
@@ -9,8 +9,8 @@ junitVersion = "1.2.1"
espressoCore = "3.6.1"
appcompat = "1.7.0"
material = "1.12.0"
-activity = "1.10.0"
-constraintlayout = "2.2.0"
+activity = "1.10.1"
+constraintlayout = "2.2.1"
mmkvStatic = "1.3.12"
gson = "2.11.0"
quickieFoss = "1.13.1"
From 2ec5d8db3c254af63aed21bb9c9b7add4ba30018 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Fri, 7 Mar 2025 14:24:07 +0800
Subject: [PATCH 042/238] Update AndroidLibXrayLite
---
AndroidLibXrayLite | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/AndroidLibXrayLite b/AndroidLibXrayLite
index 36f046e2..08c4a038 160000
--- a/AndroidLibXrayLite
+++ b/AndroidLibXrayLite
@@ -1 +1 @@
-Subproject commit 36f046e27b2ffd95e9c0bc3d836bd4de7cdade9d
+Subproject commit 08c4a038f0948d9ba65f26d8ad407ba79ebfa056
From da347492d3f7de67f54e11592d5c0851eebfe9bc Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Fri, 7 Mar 2025 14:25:30 +0800
Subject: [PATCH 043/238] up 1.9.39
---
V2rayNG/app/build.gradle.kts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/V2rayNG/app/build.gradle.kts b/V2rayNG/app/build.gradle.kts
index 92d51066..9e4f169e 100644
--- a/V2rayNG/app/build.gradle.kts
+++ b/V2rayNG/app/build.gradle.kts
@@ -12,8 +12,8 @@ android {
applicationId = "com.v2ray.ang"
minSdk = 21
targetSdk = 35
- versionCode = 635
- versionName = "1.9.38"
+ versionCode = 636
+ versionName = "1.9.39"
multiDexEnabled = true
val abiFilterList = (properties["ABI_FILTERS"] as? String)?.split(';')
From 38850597f30f56191e8125061a004340cf3d0be3 Mon Sep 17 00:00:00 2001
From: DHR60
Date: Fri, 7 Mar 2025 17:45:21 +0800
Subject: [PATCH 044/238] Add kcp DNS masquerade support (#4368)
---
.../app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt | 9 ++++++++-
V2rayNG/app/src/main/res/values/arrays.xml | 1 +
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt
index 8c3b7761..e528546d 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt
@@ -257,7 +257,9 @@ data class V2rayConfig(
var header: HeaderBean = HeaderBean(),
var seed: String? = null
) {
- data class HeaderBean(var type: String = "none")
+ data class HeaderBean(
+ var type: String = "none",
+ var domain: String? = null)
}
data class WsSettingsBean(
@@ -385,6 +387,11 @@ data class V2rayConfig(
} else {
kcpsetting.seed = seed
}
+ if (host.isNullOrEmpty()) {
+ kcpsetting.header.domain = null
+ } else {
+ kcpsetting.header.domain = host
+ }
kcpSettings = kcpsetting
}
diff --git a/V2rayNG/app/src/main/res/values/arrays.xml b/V2rayNG/app/src/main/res/values/arrays.xml
index ed1c9c7d..f758f2b4 100644
--- a/V2rayNG/app/src/main/res/values/arrays.xml
+++ b/V2rayNG/app/src/main/res/values/arrays.xml
@@ -43,6 +43,7 @@
- wechat-video
- dtls
- wireguard
+ - dns
From bada7c93d719177d2b2c85748b0d4079169c7f12 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E1=A1=A0=E1=A0=B5=E1=A1=A0=E1=A1=B3=20=E1=A1=A0=E1=A0=B5?=
=?UTF-8?q?=E1=A1=A0=20=E1=A0=AE=E1=A0=A0=E1=A0=A8=E1=A1=A9=E1=A0=8B?=
=?UTF-8?q?=E1=A0=A0=E1=A0=A8?=
<125150101+UjuiUjuMandan@users.noreply.github.com>
Date: Wed, 12 Mar 2025 08:02:02 +0800
Subject: [PATCH 045/238] Allow user certificate (#4375)
* Add network security config for user CA
* Use network security config
* mv
* Suppress Inspection for user CA
---
V2rayNG/app/src/main/AndroidManifest.xml | 1 +
.../app/src/main/res/xml/network_security_config.xml | 10 ++++++++++
2 files changed, 11 insertions(+)
create mode 100644 V2rayNG/app/src/main/res/xml/network_security_config.xml
diff --git a/V2rayNG/app/src/main/AndroidManifest.xml b/V2rayNG/app/src/main/AndroidManifest.xml
index c2cde774..eae2d946 100644
--- a/V2rayNG/app/src/main/AndroidManifest.xml
+++ b/V2rayNG/app/src/main/AndroidManifest.xml
@@ -55,6 +55,7 @@
android:supportsRtl="true"
android:theme="@style/AppThemeDayNight"
android:usesCleartextTraffic="true"
+ android:networkSecurityConfig="@xml/network_security_config"
tools:targetApi="m">
+
+
+
+
+
+
+
+
From 5e993fd91bfbd158dedc3941fec8e6f84ae7515a Mon Sep 17 00:00:00 2001
From: Noo6 <72285529+No06@users.noreply.github.com>
Date: Wed, 12 Mar 2025 08:02:35 +0800
Subject: [PATCH 046/238] Fix subscription link redirect handling (#4378)
* Fix subscription link redirect handling
* optimize code format
---
.../src/main/java/com/v2ray/ang/util/Utils.kt | 73 +++++++++++++------
1 file changed, 50 insertions(+), 23 deletions(-)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/util/Utils.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/util/Utils.kt
index ee0167fd..9f951df0 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/util/Utils.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/util/Utils.kt
@@ -364,31 +364,58 @@ object Utils {
timeout: Int = 30000,
httpPort: Int = 0
): String {
- val url = URL(urlStr)
- val conn = if (httpPort == 0) {
- url.openConnection()
- } else {
- url.openConnection(
- Proxy(
- Proxy.Type.HTTP,
- InetSocketAddress(LOOPBACK, httpPort)
+ var currentUrl = urlStr
+ var redirects = 0
+ val maxRedirects = 5
+
+ while (redirects < maxRedirects) {
+ val url = URL(currentUrl)
+ val conn = if (httpPort == 0) {
+ url.openConnection()
+ } else {
+ url.openConnection(
+ Proxy(
+ Proxy.Type.HTTP,
+ InetSocketAddress(LOOPBACK, httpPort)
+ )
)
- )
- }
- conn.connectTimeout = timeout
- conn.readTimeout = timeout
- conn.setRequestProperty("Connection", "close")
- conn.setRequestProperty("User-agent", "v2rayNG/${BuildConfig.VERSION_NAME}")
- url.userInfo?.let {
- conn.setRequestProperty(
- "Authorization",
- "Basic ${encode(urlDecode(it))}"
- )
- }
- conn.useCaches = false
- return conn.inputStream.use {
- it.bufferedReader().readText()
+ } as HttpURLConnection
+
+ conn.connectTimeout = timeout
+ conn.readTimeout = timeout
+ conn.setRequestProperty("Connection", "close")
+ conn.setRequestProperty("User-agent", "v2rayNG/${BuildConfig.VERSION_NAME}")
+ url.userInfo?.let {
+ conn.setRequestProperty(
+ "Authorization",
+ "Basic ${encode(urlDecode(it))}"
+ )
+ }
+ conn.useCaches = false
+ conn.instanceFollowRedirects = false
+
+ conn.connect()
+
+ val responseCode = conn.responseCode
+ when (responseCode) {
+ in 300..399 -> {
+ val location = conn.getHeaderField("Location")
+ conn.disconnect()
+ if (location.isNullOrEmpty()) {
+ throw IOException("Redirect location not found")
+ }
+ currentUrl = location
+ redirects++
+ continue
+ }
+ else -> try {
+ return conn.inputStream.use { it.bufferedReader().readText() }
+ } finally {
+ conn.disconnect()
+ }
+ }
}
+ throw IOException("Too many redirects")
}
fun getDarkModeStatus(context: Context): Boolean {
From d1904d52d93862c115f27649a2d3b7a86dbe1b58 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Sun, 16 Mar 2025 10:24:32 +0800
Subject: [PATCH 047/238] Bug fix
Exception java.lang.StackOverflowError: stack size 8188KB
---
V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt
index 8b7b336c..54b6a520 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt
@@ -192,8 +192,9 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
if (binding.drawerLayout.isDrawerOpen(GravityCompat.START)) {
binding.drawerLayout.closeDrawer(GravityCompat.START)
} else {
- //super.onBackPressed()
+ isEnabled = false
onBackPressedDispatcher.onBackPressed()
+ isEnabled = true
}
}
})
From 159a3702867e085a0bf035cc196219db0551f0f6 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Sun, 16 Mar 2025 14:44:25 +0800
Subject: [PATCH 048/238] Changed tun2socks config private vlan
https://github.com/2dust/v2rayNG/issues/4373
---
.../main/java/com/v2ray/ang/service/V2RayVpnService.kt | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayVpnService.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayVpnService.kt
index 41a5745d..82b57a36 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayVpnService.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayVpnService.kt
@@ -35,10 +35,10 @@ import java.lang.ref.SoftReference
class V2RayVpnService : VpnService(), ServiceControl {
companion object {
private const val VPN_MTU = 1500
- private const val PRIVATE_VLAN4_CLIENT = "10.10.10.1"
- private const val PRIVATE_VLAN4_ROUTER = "10.10.10.2"
- private const val PRIVATE_VLAN6_CLIENT = "fc00::10:10:10:1"
- private const val PRIVATE_VLAN6_ROUTER = "fc00::10:10:10:2"
+ private const val PRIVATE_VLAN4_CLIENT = "10.10.14.1"
+ private const val PRIVATE_VLAN4_ROUTER = "10.10.14.2"
+ private const val PRIVATE_VLAN6_CLIENT = "fc00::10:10:14:1"
+ private const val PRIVATE_VLAN6_ROUTER = "fc00::10:10:14:2"
private const val TUN2SOCKS = "libtun2socks.so"
}
From ca32ee3a0eafe6b16d4332f0f966b556d2bb9cad Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Mon, 17 Mar 2025 20:55:08 +0800
Subject: [PATCH 049/238] Update proxy_packagename.txt
---
.../app/src/main/assets/proxy_packagename.txt | 174 +++++++++++++++++-
1 file changed, 173 insertions(+), 1 deletion(-)
diff --git a/V2rayNG/app/src/main/assets/proxy_packagename.txt b/V2rayNG/app/src/main/assets/proxy_packagename.txt
index ecac8251..12abf7db 100644
--- a/V2rayNG/app/src/main/assets/proxy_packagename.txt
+++ b/V2rayNG/app/src/main/assets/proxy_packagename.txt
@@ -4,6 +4,7 @@ au.com.shiftyjelly.pocketcasts
bbc.mobile.news.ww
be.mygod.vpnhotspot
ch.protonmail.android
+cm.aptoide.pt
co.wanqu.android
com.alphainventor.filemanager
com.amazon.kindle
@@ -34,7 +35,9 @@ com.chrome.canary
com.chrome.dev
com.cl.newt66y
com.cradle.iitc_mobile
+org.exarhteam.iitc_mobile
com.cygames.shadowverse
+com.dcard.freedom
com.devhd.feedly
com.devolver.reigns2
com.discord
@@ -108,6 +111,7 @@ com.ifttt.ifttt
com.imgur.mobile
com.innologica.inoreader
com.instagram.android
+com.instagram.lite
com.instapaper.android
com.jarvanh.vpntether
com.kapp.youtube.final
@@ -115,6 +119,7 @@ com.klinker.android.twitter_l
com.lastpass.lpandroid
com.linecorp.linelite
com.lingodeer
+com.ltnnews.news
com.mediapods.tumbpods
com.mgoogle.android.gms
com.microsoft.emmx
@@ -159,6 +164,7 @@ com.slack
com.snaptube.premium
com.sololearn
com.sonelli.juicessh
+com.sparkslab.dcardreader
com.spotify.music
com.tencent.huatuo
com.termux
@@ -173,10 +179,13 @@ com.twitter.android
com.u91porn
com.u9porn
com.ubisoft.dance.justdance2015companion
+com.udn.news
com.utopia.pxview
-com.valvesoftware.android.steam.communimunity
com.valvesoftware.android.steam.community
+com.vanced.manager
com.vanced.android.youtube
+com.vanced.android.apps.youtube.music
+com.mgoogle.android.gms
com.vimeo.android.videoapp
com.vivaldi.browser
com.vivaldi.browser.snapshot
@@ -186,10 +195,12 @@ com.wire
com.wuxiangai.refactor
com.xda.labs
com.xvideos.app
+com.yahoo.mobile.client.android.superapp
com.yandex.browser
com.yandex.browser.beta
com.yandex.browser.alpha
com.z28j.feel
+com.zhiliaoapp.musically
con.medium.reader
de.apkgrabber
de.robv.android.xposed.installer
@@ -210,6 +221,7 @@ jp.bokete.app.android
jp.naver.line.android
jp.pxv.android
luo.speedometergpspro
+m.cna.com.tw.App
mark.via.gp
me.tshine.easymark
net.teeha.android.url_shortener
@@ -226,6 +238,7 @@ org.mozilla.firefox_beta
org.mozilla.focus
org.schabi.newpipe
org.telegram.messenger
+org.telegram.messenger.web
org.telegram.multi
org.telegram.plus
org.thunderdog.challegram
@@ -239,3 +252,162 @@ tw.com.gamer.android.activecenter
videodownloader.downloadvideo.downloader
uk.co.bbc.learningenglish
com.ted.android
+de.danoeh.antennapod
+com.kiwibrowser.browser
+nekox.messenger
+com.nextcloud.client
+com.aurora.store
+com.aurora.adroid
+chat.simplex.app
+im.vector.app
+network.loki.messenger
+eu.siacs.conversations
+xyz.nextalone.nagram
+net.programmierecke.radiodroid2
+im.fdx.v2ex
+ml.docilealligator.infinityforreddit
+com.bytemyth.ama
+app.vanadium.browser
+com.cakewallet.cake_wallet
+org.purplei2p.i2pd
+dk.tacit.android.foldersync.lite
+com.nononsenseapps.feeder
+com.m2049r.xmrwallet
+com.paypal.android.p2pmobile
+com.google.android.apps.googlevoice
+com.readdle.spark
+org.torproject.torbrowser
+com.deepl.mobiletranslator
+com.microsoft.bing
+com.keylesspalace.tusky
+com.ottplay.ottplay
+ru.iptvremote.android.iptv.pro
+jp.naver.line.android
+com.xmflsct.app.tooot
+com.forem.android
+app.revanced.android.youtube
+com.mgoogle.android.gms
+com.pionex.client
+vip.mytokenpocket
+im.token.app
+com.linekong.mars24
+com.feixiaohao
+com.aicoin.appandroid
+com.binance.dev
+com.kraken.trade
+com.okinc.okex.gp
+com.authy.authy
+air.com.rosettastone.mobile.CoursePlayer
+com.blizzard.bma
+com.amazon.kindle
+com.google.android.apps.fitness
+net.tsapps.appsales
+com.wemesh.android
+com.google.android.apps.googleassistant
+allen.town.focus.reader
+me.hyliu.fluent_reader_lite
+com.aljazeera.mobile
+com.ft.news
+de.marmaro.krt.ffupdater
+myradio.radio.fmradio.liveradio.radiostation
+com.google.earth
+eu.kanade.tachiyomi.j2k
+com.audials
+com.microsoft.skydrive
+com.mb.android.tg
+com.melodis.midomiMusicIdentifier.freemium
+com.foxnews.android
+ch.threema.app
+com.briarproject.briar.android
+foundation.e.apps
+com.valvesoftware.android.steam.friendsui
+com.imback.yeetalk
+so.onekey.app.wallet
+com.xc3fff0e.xmanager
+meditofoundation.medito
+com.picol.client
+com.streetwriters.notesnook
+shanghai.panewsApp.com
+org.coursera.android
+com.positron_it.zlib
+com.blizzard.messenger
+com.javdb.javrocket
+com.picacomic.fregata
+com.fxl.chacha
+me.proton.android.drive
+com.lastpass.lpandroid
+com.tradingview.tradingviewapp
+com.deviantart.android.damobile
+com.fusionmedia.investing
+com.ewa.ewaapp
+com.duolingo
+com.hellotalk
+io.github.huskydg.magisk
+com.jsy.xpgbox
+com.hostloc.app.hostloc
+com.dena.pokota
+com.vitorpamplona.amethyst
+com.zhiliaoapp.musically
+us.spotco.fennec_dos
+com.fongmi.android.tv
+com.pocketprep.android.itcybersecurity
+com.cloudtv
+com.glassdoor.app
+com.indeed.android.jobsearch
+com.linkedin.android
+com.github.tvbox.osc.bh
+com.example.douban
+com.sipnetic.app
+com.microsoft.rdc.androidx
+org.zwanoo.android.speedtest
+com.sonelli.juicessh
+com.scmp.newspulse
+org.lsposed.manager
+mnn.Android
+com.thomsonretuers.reuters
+com.guardian
+com.ttxapps.onesyncv2
+org.fcitx.fcitx5.android.updater
+com.tailscale.ipn
+tw.nekomimi.nekogram
+com.nexon.kartdrift
+io.syncapps.lemmy_sync
+com.seazon.feedme
+com.readwise
+de.spiritcroc.riotx
+com.openai.chatgpt
+io.changenow.changenow
+com.poe.android
+com.twingate
+com.blinkslabs.blinkist.android
+com.ichi2.anki
+md.obsidian
+com.musixmatch.android.lyrify
+com.cyber.turbo
+com.offsec.nethunter
+me.ghui.v2er
+com.samruston.twitter
+org.adaway
+org.swiftapps.swiftbackup
+com.zerotier.one
+com.quietmobile
+com.instagram.barcelona
+im.molly.app
+com.rvx.android.youtube
+com.deepl.mobiletranslator
+com.qingsong.yingmi
+com.lemurbrowser.exts
+com.silverdev.dnartdroid
+me.ash.reader
+de.tutao.tutanota
+dev.imranr.obtainium
+com.getsomeheadspace.android
+org.cromite.cromite
+com.nutomic.syncthingandroid
+com.bumble.app
+com.cnn.mobile.android.phone
+com.google.android.apps.authenticator2
+com.microsoft.copilot
+com.netflix.NGP.Storyteller
+com.Slack
+com.server.auditor.ssh.client
\ No newline at end of file
From 55a11bbeee7bf896633949ea2b8e4947717c8e58 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Tue, 18 Mar 2025 09:31:00 +0800
Subject: [PATCH 050/238] Update libs.versions.toml
---
V2rayNG/gradle/libs.versions.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/V2rayNG/gradle/libs.versions.toml b/V2rayNG/gradle/libs.versions.toml
index 79a837df..6cc79019 100644
--- a/V2rayNG/gradle/libs.versions.toml
+++ b/V2rayNG/gradle/libs.versions.toml
@@ -1,5 +1,5 @@
[versions]
-agp = "8.8.2"
+agp = "8.9.0"
desugar_jdk_libs = "2.1.5"
gradleLicensePlugin = "0.9.8"
kotlin = "2.1.10"
From 1972f83b868e7ead9681f14cdbf1fa150404ec19 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Tue, 18 Mar 2025 09:55:40 +0800
Subject: [PATCH 051/238] Add HttpUtil, refactor the network connection
function
---
.../com/v2ray/ang/handler/AngConfigManager.kt | 7 +-
.../java/com/v2ray/ang/ui/MainActivity.kt | 5 +-
.../com/v2ray/ang/ui/PerAppProxyActivity.kt | 3 +-
.../com/v2ray/ang/ui/UserAssetActivity.kt | 25 +---
.../main/java/com/v2ray/ang/util/HttpUtil.kt | 126 ++++++++++++++++++
.../java/com/v2ray/ang/util/SpeedtestUtil.kt | 21 +--
.../src/main/java/com/v2ray/ang/util/Utils.kt | 88 ------------
7 files changed, 142 insertions(+), 133 deletions(-)
create mode 100644 V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/AngConfigManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/AngConfigManager.kt
index 32b19f00..865a0817 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/AngConfigManager.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/AngConfigManager.kt
@@ -16,6 +16,7 @@ import com.v2ray.ang.fmt.TrojanFmt
import com.v2ray.ang.fmt.VlessFmt
import com.v2ray.ang.fmt.VmessFmt
import com.v2ray.ang.fmt.WireguardFmt
+import com.v2ray.ang.util.HttpUtil
import com.v2ray.ang.util.JsonUtil
import com.v2ray.ang.util.QRCodeDecoder
import com.v2ray.ang.util.Utils
@@ -346,7 +347,7 @@ object AngConfigManager {
if (!it.second.enabled) {
return 0
}
- val url = Utils.idnToASCII(it.second.url)
+ val url = HttpUtil.idnToASCII(it.second.url)
if (!Utils.isValidUrl(url)) {
return 0
}
@@ -354,7 +355,7 @@ object AngConfigManager {
var configText = try {
val httpPort = SettingsManager.getHttpPort()
- Utils.getUrlContentWithCustomUserAgent(url, 30000, httpPort)
+ HttpUtil.getUrlContentWithUserAgent(url, 30000, httpPort)
} catch (e: Exception) {
Log.e(AppConfig.ANG_PACKAGE, "Update subscription: proxy not ready or other error, try……")
//e.printStackTrace()
@@ -362,7 +363,7 @@ object AngConfigManager {
}
if (configText.isEmpty()) {
configText = try {
- Utils.getUrlContentWithCustomUserAgent(url)
+ HttpUtil.getUrlContentWithUserAgent(url)
} catch (e: Exception) {
e.printStackTrace()
""
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt
index 54b6a520..f9fdce8e 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt
@@ -1,7 +1,6 @@
package com.v2ray.ang.ui
import android.Manifest
-import android.content.ActivityNotFoundException
import android.content.Intent
import android.content.pm.PackageManager
import android.content.res.ColorStateList
@@ -39,6 +38,7 @@ import com.v2ray.ang.handler.MigrateManager
import com.v2ray.ang.handler.MmkvManager
import com.v2ray.ang.helper.SimpleItemTouchHelperCallback
import com.v2ray.ang.service.V2RayServiceManager
+import com.v2ray.ang.util.HttpUtil
import com.v2ray.ang.util.Utils
import com.v2ray.ang.viewmodel.MainViewModel
import kotlinx.coroutines.Dispatchers
@@ -46,7 +46,6 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import me.drakeet.support.toast.ToastCompat
-import java.util.concurrent.TimeUnit
class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedListener {
private val binding by lazy {
@@ -626,7 +625,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
}
lifecycleScope.launch(Dispatchers.IO) {
val configText = try {
- Utils.getUrlContentWithCustomUserAgent(url)
+ HttpUtil.getUrlContentWithUserAgent(url)
} catch (e: Exception) {
e.printStackTrace()
""
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/PerAppProxyActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/PerAppProxyActivity.kt
index 10e1a10f..a6fdfdef 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/PerAppProxyActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/PerAppProxyActivity.kt
@@ -24,6 +24,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.text.Collator
+import com.v2ray.ang.util.HttpUtil
class PerAppProxyActivity : BaseActivity() {
private val binding by lazy {
@@ -154,7 +155,7 @@ class PerAppProxyActivity : BaseActivity() {
toast(R.string.msg_downloading_content)
val url = AppConfig.androidpackagenamelistUrl
lifecycleScope.launch(Dispatchers.IO) {
- val content = Utils.getUrlContext(url, 5000)
+ val content = HttpUtil.getUrlContent(url, 5000)
launch(Dispatchers.Main) {
Log.d(ANG_PACKAGE, content)
selectProxyApp(content, true)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/UserAssetActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/UserAssetActivity.kt
index 06d371e6..ae197a60 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/UserAssetActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/UserAssetActivity.kt
@@ -20,7 +20,6 @@ import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.v2ray.ang.AppConfig
-import com.v2ray.ang.AppConfig.LOOPBACK
import com.v2ray.ang.R
import com.v2ray.ang.databinding.ActivitySubSettingBinding
import com.v2ray.ang.databinding.ItemRecyclerUserAssetBinding
@@ -30,6 +29,7 @@ import com.v2ray.ang.extension.toTrafficString
import com.v2ray.ang.extension.toast
import com.v2ray.ang.handler.MmkvManager
import com.v2ray.ang.handler.SettingsManager
+import com.v2ray.ang.util.HttpUtil
import com.v2ray.ang.util.Utils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -37,9 +37,6 @@ import kotlinx.coroutines.withContext
import java.io.File
import java.io.FileOutputStream
import java.net.HttpURLConnection
-import java.net.InetSocketAddress
-import java.net.Proxy
-import java.net.URL
import java.text.DateFormat
import java.util.Date
@@ -186,8 +183,10 @@ class UserAssetActivity : BaseActivity() {
return false
}
// Send URL to UserAssetUrlActivity for Processing
- startActivity(Intent(this, UserAssetUrlActivity::class.java)
- .putExtra(UserAssetUrlActivity.ASSET_URL_QRCODE, url))
+ startActivity(
+ Intent(this, UserAssetUrlActivity::class.java)
+ .putExtra(UserAssetUrlActivity.ASSET_URL_QRCODE, url)
+ )
} catch (e: Exception) {
e.printStackTrace()
return false
@@ -229,22 +228,10 @@ class UserAssetActivity : BaseActivity() {
private fun downloadGeo(item: AssetUrlItem, timeout: Int, httpPort: Int): Boolean {
val targetTemp = File(extDir, item.remarks + "_temp")
val target = File(extDir, item.remarks)
- var conn: HttpURLConnection? = null
//Log.d(AppConfig.ANG_PACKAGE, url)
+ val conn = HttpUtil.createProxyConnection(item.url, httpPort, timeout, timeout, needStream = true) ?: return false
try {
- conn = if (httpPort == 0) {
- URL(item.url).openConnection() as HttpURLConnection
- } else {
- URL(item.url).openConnection(
- Proxy(
- Proxy.Type.HTTP,
- InetSocketAddress(LOOPBACK, httpPort)
- )
- ) as HttpURLConnection
- }
- conn.connectTimeout = timeout
- conn.readTimeout = timeout
val inputStream = conn.inputStream
val responseCode = conn.responseCode
if (responseCode == HttpURLConnection.HTTP_OK) {
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt
new file mode 100644
index 00000000..d3d41458
--- /dev/null
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt
@@ -0,0 +1,126 @@
+package com.v2ray.ang.util
+
+import com.v2ray.ang.AppConfig.LOOPBACK
+import com.v2ray.ang.BuildConfig
+import com.v2ray.ang.util.Utils.encode
+import com.v2ray.ang.util.Utils.urlDecode
+import java.io.IOException
+import java.net.*
+import java.util.*
+
+object HttpUtil {
+
+ fun idnToASCII(str: String): String {
+ val url = URL(str)
+ return URL(url.protocol, IDN.toASCII(url.host, IDN.ALLOW_UNASSIGNED), url.port, url.file).toExternalForm()
+ }
+
+ fun getUrlContent(url: String, timeout: Int): String {
+ var result: String = ""
+ val conn = createProxyConnection(url, 0, timeout, timeout) ?: return result
+ try {
+ result = conn.inputStream.bufferedReader().readText()
+ } catch (_: Exception) {
+ } finally {
+ conn.disconnect()
+ }
+ return result
+ }
+
+ @Throws(IOException::class)
+ fun getUrlContentWithUserAgent(url: String?, timeout: Int = 30000, httpPort: Int = 0): String {
+ var currentUrl = url
+ var redirects = 0
+ val maxRedirects = 3
+
+ while (redirects++ < maxRedirects) {
+ if (currentUrl == null) continue
+ val conn = createProxyConnection(currentUrl, httpPort, timeout, timeout) ?: continue
+ conn.setRequestProperty("User-agent", "v2rayNG/${BuildConfig.VERSION_NAME}")
+ conn.connect()
+
+ val responseCode = conn.responseCode
+ when (responseCode) {
+ in 300..399 -> {
+ val location = conn.getHeaderField("Location")
+ conn.disconnect()
+ if (location.isNullOrEmpty()) {
+ throw IOException("Redirect location not found")
+ }
+ currentUrl = location
+ continue
+ }
+
+ else -> try {
+ return conn.inputStream.use { it.bufferedReader().readText() }
+ } finally {
+ conn.disconnect()
+ }
+ }
+ }
+ throw IOException("Too many redirects")
+ }
+
+ /**
+ * Creates an HttpURLConnection object connected through a proxy.
+ *
+ * @param urlStr The target URL address.
+ * @param ip The IP address of the proxy server.
+ * @param port The port of the proxy server.
+ * @param connectTimeout The connection timeout in milliseconds (default is 30000 ms).
+ * @param readTimeout The read timeout in milliseconds (default is 30000 ms).
+ * @param needStream
+ * @return Returns a configured HttpURLConnection object, or null if it fails.
+ */
+ fun createProxyConnection(
+ urlStr: String,
+ port: Int,
+ connectTimeout: Int = 30000,
+ readTimeout: Int = 30000,
+ needStream: Boolean = false
+ ): HttpURLConnection? {
+
+ var conn: HttpURLConnection? = null
+ try {
+ val url = URL(urlStr)
+ // Create a connection
+ conn = if (port == 0) {
+ url.openConnection()
+ } else {
+ url.openConnection(
+ Proxy(
+ Proxy.Type.HTTP,
+ InetSocketAddress(LOOPBACK, port)
+ )
+ )
+ } as HttpURLConnection
+
+ // Set connection and read timeouts
+ conn.connectTimeout = connectTimeout
+ conn.readTimeout = readTimeout
+ if (!needStream) {
+ // Set request headers
+ conn.setRequestProperty("Connection", "close")
+ // Disable automatic redirects
+ conn.instanceFollowRedirects = false
+ // Disable caching
+ conn.useCaches = false
+ }
+
+ //Add Basic Authorization
+ url.userInfo?.let {
+ conn.setRequestProperty(
+ "Authorization",
+ "Basic ${encode(urlDecode(it))}"
+ )
+ }
+ } catch (e: Exception) {
+ e.printStackTrace()
+ // If an exception occurs, close the connection and return null
+ conn?.disconnect()
+ return null
+ }
+ return conn
+ }
+}
+
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/util/SpeedtestUtil.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/util/SpeedtestUtil.kt
index 10e793be..d1b13d21 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/util/SpeedtestUtil.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/util/SpeedtestUtil.kt
@@ -5,17 +5,14 @@ import android.os.SystemClock
import android.text.TextUtils
import android.util.Log
import com.v2ray.ang.AppConfig
-import com.v2ray.ang.AppConfig.LOOPBACK
import com.v2ray.ang.R
import com.v2ray.ang.extension.responseLength
+import com.v2ray.ang.util.HttpUtil
import kotlinx.coroutines.isActive
import libv2ray.Libv2ray
import java.io.IOException
-import java.net.HttpURLConnection
import java.net.InetSocketAddress
-import java.net.Proxy
import java.net.Socket
-import java.net.URL
import java.net.UnknownHostException
import kotlin.coroutines.coroutineContext
@@ -101,23 +98,9 @@ object SpeedtestUtil {
fun testConnection(context: Context, port: Int): Pair {
var result: String
var elapsed = -1L
- var conn: HttpURLConnection? = null
+ val conn = HttpUtil.createProxyConnection(Utils.getDelayTestUrl(), port, 30000, 30000) ?: return Pair(elapsed, "")
try {
- val url = URL(Utils.getDelayTestUrl())
-
- conn = url.openConnection(
- Proxy(
- Proxy.Type.HTTP,
- InetSocketAddress(LOOPBACK, port)
- )
- ) as HttpURLConnection
- conn.connectTimeout = 30000
- conn.readTimeout = 30000
- conn.setRequestProperty("Connection", "close")
- conn.instanceFollowRedirects = false
- conn.useCaches = false
-
val start = SystemClock.elapsedRealtime()
val code = conn.responseCode
elapsed = SystemClock.elapsedRealtime() - start
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/util/Utils.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/util/Utils.kt
index 9f951df0..73a70840 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/util/Utils.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/util/Utils.kt
@@ -21,7 +21,6 @@ import androidx.core.content.ContextCompat
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
import com.v2ray.ang.dto.Language
import com.v2ray.ang.extension.toast
@@ -337,87 +336,6 @@ object Utils {
return Base64.encodeToString(androidId.copyOf(32), Base64.NO_PADDING.or(Base64.URL_SAFE))
}
- fun getUrlContext(url: String, timeout: Int): String {
- var result: String
- var conn: HttpURLConnection? = null
-
- try {
- conn = URL(url).openConnection() as HttpURLConnection
- conn.connectTimeout = timeout
- conn.readTimeout = timeout
- conn.setRequestProperty("Connection", "close")
- conn.instanceFollowRedirects = false
- conn.useCaches = false
- //val code = conn.responseCode
- result = conn.inputStream.bufferedReader().readText()
- } catch (e: Exception) {
- result = ""
- } finally {
- conn?.disconnect()
- }
- return result
- }
-
- @Throws(IOException::class)
- fun getUrlContentWithCustomUserAgent(
- urlStr: String?,
- timeout: Int = 30000,
- httpPort: Int = 0
- ): String {
- var currentUrl = urlStr
- var redirects = 0
- val maxRedirects = 5
-
- while (redirects < maxRedirects) {
- val url = URL(currentUrl)
- val conn = if (httpPort == 0) {
- url.openConnection()
- } else {
- url.openConnection(
- Proxy(
- Proxy.Type.HTTP,
- InetSocketAddress(LOOPBACK, httpPort)
- )
- )
- } as HttpURLConnection
-
- conn.connectTimeout = timeout
- conn.readTimeout = timeout
- conn.setRequestProperty("Connection", "close")
- conn.setRequestProperty("User-agent", "v2rayNG/${BuildConfig.VERSION_NAME}")
- url.userInfo?.let {
- conn.setRequestProperty(
- "Authorization",
- "Basic ${encode(urlDecode(it))}"
- )
- }
- conn.useCaches = false
- conn.instanceFollowRedirects = false
-
- conn.connect()
-
- val responseCode = conn.responseCode
- when (responseCode) {
- in 300..399 -> {
- val location = conn.getHeaderField("Location")
- conn.disconnect()
- if (location.isNullOrEmpty()) {
- throw IOException("Redirect location not found")
- }
- currentUrl = location
- redirects++
- continue
- }
- else -> try {
- return conn.inputStream.use { it.bufferedReader().readText() }
- } finally {
- conn.disconnect()
- }
- }
- }
- throw IOException("Too many redirects")
- }
-
fun getDarkModeStatus(context: Context): Boolean {
return context.resources.configuration.uiMode and UI_MODE_NIGHT_MASK != UI_MODE_NIGHT_NO
}
@@ -477,12 +395,6 @@ object Utils {
return str?.replace(" ", "")
}
- fun idnToASCII(str: String): String {
- val url = URL(str)
- return URL(url.protocol, IDN.toASCII(url.host, IDN.ALLOW_UNASSIGNED), url.port, url.file)
- .toExternalForm()
- }
-
fun isTv(context: Context): Boolean =
context.packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
From e14b48f3ebf8a5b8aaf58d8aa39c4ec6eb350616 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Tue, 18 Mar 2025 15:04:25 +0800
Subject: [PATCH 052/238] Code clean
---
V2rayNG/app/src/main/java/com/v2ray/ang/AngApplication.kt | 2 --
V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt | 6 ++++--
V2rayNG/app/src/main/java/com/v2ray/ang/fmt/HttpFmt.kt | 1 -
V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt | 1 -
V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt | 1 -
V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt | 1 -
V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt | 1 -
.../src/main/java/com/v2ray/ang/handler/SettingsManager.kt | 1 -
.../com/v2ray/ang/helper/SimpleItemTouchHelperCallback.kt | 1 -
V2rayNG/app/src/main/java/com/v2ray/ang/ui/AboutActivity.kt | 1 -
.../src/main/java/com/v2ray/ang/ui/PerAppProxyActivity.kt | 2 +-
.../app/src/main/java/com/v2ray/ang/ui/ScannerActivity.kt | 1 -
.../app/src/main/java/com/v2ray/ang/ui/UrlSchemeActivity.kt | 1 -
.../app/src/main/java/com/v2ray/ang/util/AppManagerUtil.kt | 2 +-
.../app/src/main/java/com/v2ray/ang/util/SpeedtestUtil.kt | 1 -
15 files changed, 6 insertions(+), 17 deletions(-)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/AngApplication.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/AngApplication.kt
index 6b437021..a999a9b4 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/AngApplication.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/AngApplication.kt
@@ -1,8 +1,6 @@
package com.v2ray.ang
import android.content.Context
-import android.content.pm.PackageManager
-import android.os.Build
import androidx.multidex.MultiDexApplication
import androidx.work.Configuration
import androidx.work.WorkManager
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt
index e528546d..a602b940 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt
@@ -8,8 +8,10 @@ import com.google.gson.JsonSerializer
import com.google.gson.annotations.SerializedName
import com.google.gson.reflect.TypeToken
import com.v2ray.ang.AppConfig
-import com.v2ray.ang.dto.V2rayConfig.OutboundBean.OutSettingsBean.*
-import com.v2ray.ang.dto.V2rayConfig.OutboundBean.OutSettingsBean.VnextBean.*
+import com.v2ray.ang.dto.V2rayConfig.OutboundBean.OutSettingsBean.ServersBean
+import com.v2ray.ang.dto.V2rayConfig.OutboundBean.OutSettingsBean.VnextBean
+import com.v2ray.ang.dto.V2rayConfig.OutboundBean.OutSettingsBean.VnextBean.UsersBean
+import com.v2ray.ang.dto.V2rayConfig.OutboundBean.OutSettingsBean.WireGuardBean
import com.v2ray.ang.util.Utils
import java.lang.reflect.Type
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/HttpFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/HttpFmt.kt
index 0c85b171..e9ebc53e 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/HttpFmt.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/HttpFmt.kt
@@ -4,7 +4,6 @@ import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.dto.ProfileItem
import com.v2ray.ang.dto.V2rayConfig.OutboundBean
import com.v2ray.ang.extension.isNotNullEmpty
-import kotlin.text.orEmpty
object HttpFmt : FmtBase() {
fun toOutbound(profileItem: ProfileItem): OutboundBean? {
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt
index b610a9da..3b47bf3e 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt
@@ -7,7 +7,6 @@ import com.v2ray.ang.extension.idnHost
import com.v2ray.ang.extension.isNotNullEmpty
import com.v2ray.ang.util.Utils
import java.net.URI
-import kotlin.text.orEmpty
object SocksFmt : FmtBase() {
fun parse(str: String): ProfileItem? {
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt
index 7139ada0..db0c907d 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt
@@ -9,7 +9,6 @@ import com.v2ray.ang.extension.idnHost
import com.v2ray.ang.handler.MmkvManager
import com.v2ray.ang.util.Utils
import java.net.URI
-import kotlin.text.orEmpty
object TrojanFmt : FmtBase() {
fun parse(str: String): ProfileItem? {
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt
index 72a5868a..e566f5fa 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt
@@ -14,7 +14,6 @@ import com.v2ray.ang.handler.MmkvManager
import com.v2ray.ang.util.JsonUtil
import com.v2ray.ang.util.Utils
import java.net.URI
-import kotlin.text.orEmpty
object VmessFmt : FmtBase() {
fun parse(str: String): ProfileItem? {
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt
index 11231887..2bce4d24 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt
@@ -8,7 +8,6 @@ import com.v2ray.ang.dto.V2rayConfig.OutboundBean
import com.v2ray.ang.extension.idnHost
import com.v2ray.ang.util.Utils
import java.net.URI
-import kotlin.text.orEmpty
object WireguardFmt : FmtBase() {
fun parse(str: String): ProfileItem? {
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SettingsManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SettingsManager.kt
index 83376356..cf116a4d 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SettingsManager.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SettingsManager.kt
@@ -22,7 +22,6 @@ import com.v2ray.ang.util.Utils.parseInt
import java.io.File
import java.io.FileOutputStream
import java.util.Collections
-import kotlin.Int
object SettingsManager {
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/helper/SimpleItemTouchHelperCallback.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/helper/SimpleItemTouchHelperCallback.kt
index ff61aadc..44a9853b 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/helper/SimpleItemTouchHelperCallback.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/helper/SimpleItemTouchHelperCallback.kt
@@ -16,7 +16,6 @@
package com.v2ray.ang.helper
import android.animation.ValueAnimator
-import android.animation.ValueAnimator.AnimatorUpdateListener
import android.graphics.Canvas
import android.view.animation.DecelerateInterpolator
import androidx.recyclerview.widget.GridLayoutManager
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/AboutActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/AboutActivity.kt
index 806758af..69838ac2 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/AboutActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/AboutActivity.kt
@@ -7,7 +7,6 @@ import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
-import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import com.tencent.mmkv.MMKV
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/PerAppProxyActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/PerAppProxyActivity.kt
index a6fdfdef..e59cd537 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/PerAppProxyActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/PerAppProxyActivity.kt
@@ -19,12 +19,12 @@ import com.v2ray.ang.extension.toast
import com.v2ray.ang.extension.v2RayApplication
import com.v2ray.ang.handler.MmkvManager
import com.v2ray.ang.util.AppManagerUtil
+import com.v2ray.ang.util.HttpUtil
import com.v2ray.ang.util.Utils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.text.Collator
-import com.v2ray.ang.util.HttpUtil
class PerAppProxyActivity : BaseActivity() {
private val binding by lazy {
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ScannerActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ScannerActivity.kt
index 829d345d..6702cdc1 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ScannerActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ScannerActivity.kt
@@ -9,7 +9,6 @@ import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import androidx.activity.result.contract.ActivityResultContracts
-import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/UrlSchemeActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/UrlSchemeActivity.kt
index 2a287d11..8d5c2b42 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/UrlSchemeActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/UrlSchemeActivity.kt
@@ -10,7 +10,6 @@ import com.v2ray.ang.databinding.ActivityLogcatBinding
import com.v2ray.ang.extension.toast
import com.v2ray.ang.handler.AngConfigManager
import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.net.URLDecoder
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/util/AppManagerUtil.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/util/AppManagerUtil.kt
index e2300512..e813384d 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/util/AppManagerUtil.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/util/AppManagerUtil.kt
@@ -4,8 +4,8 @@ import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import com.v2ray.ang.dto.AppInfo
-import kotlinx.coroutines.withContext
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
object AppManagerUtil {
suspend fun loadNetworkAppList(context: Context): ArrayList =
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/util/SpeedtestUtil.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/util/SpeedtestUtil.kt
index d1b13d21..6aae9bfc 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/util/SpeedtestUtil.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/util/SpeedtestUtil.kt
@@ -7,7 +7,6 @@ import android.util.Log
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
import com.v2ray.ang.extension.responseLength
-import com.v2ray.ang.util.HttpUtil
import kotlinx.coroutines.isActive
import libv2ray.Libv2ray
import java.io.IOException
From 401f05177418972f2a8be32b9bb892452ef1769c Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Tue, 18 Mar 2025 15:04:33 +0800
Subject: [PATCH 053/238] Update AndroidLibXrayLite
---
AndroidLibXrayLite | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/AndroidLibXrayLite b/AndroidLibXrayLite
index 08c4a038..d10cf1a4 160000
--- a/AndroidLibXrayLite
+++ b/AndroidLibXrayLite
@@ -1 +1 @@
-Subproject commit 08c4a038f0948d9ba65f26d8ad407ba79ebfa056
+Subproject commit d10cf1a43fe9d7f8a4cc10e1b7009d09b07542a2
From 5f3d7c02138393036eb96eaf50d6d26032b6d364 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Tue, 18 Mar 2025 15:05:42 +0800
Subject: [PATCH 054/238] Refactor the start and stop service
---
.../com/v2ray/ang/receiver/BootReceiver.kt | 2 +-
.../com/v2ray/ang/receiver/TaskerReceiver.kt | 9 ++---
.../com/v2ray/ang/receiver/WidgetProvider.kt | 5 +--
.../com/v2ray/ang/service/QSTileService.kt | 4 +-
.../v2ray/ang/service/V2RayServiceManager.kt | 27 +++++++++++--
.../java/com/v2ray/ang/ui/MainActivity.kt | 6 +--
.../com/v2ray/ang/ui/MainRecyclerAdapter.kt | 5 +--
.../java/com/v2ray/ang/ui/ScSwitchActivity.kt | 5 +--
.../src/main/java/com/v2ray/ang/util/Utils.kt | 39 +++++++------------
9 files changed, 52 insertions(+), 50 deletions(-)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/receiver/BootReceiver.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/receiver/BootReceiver.kt
index acc86730..1d96f750 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/receiver/BootReceiver.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/receiver/BootReceiver.kt
@@ -13,6 +13,6 @@ class BootReceiver : BroadcastReceiver() {
//Check if flag is true and a server is selected
if (!MmkvManager.decodeStartOnBoot() || MmkvManager.getSelectServer().isNullOrEmpty()) return
//Start v2ray
- V2RayServiceManager.startV2Ray(context)
+ V2RayServiceManager.startVService(context)
}
}
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/receiver/TaskerReceiver.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/receiver/TaskerReceiver.kt
index c6e49af4..b0aa16f4 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/receiver/TaskerReceiver.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/receiver/TaskerReceiver.kt
@@ -5,9 +5,7 @@ import android.content.Context
import android.content.Intent
import android.text.TextUtils
import com.v2ray.ang.AppConfig
-import com.v2ray.ang.handler.MmkvManager
import com.v2ray.ang.service.V2RayServiceManager
-import com.v2ray.ang.util.Utils
class TaskerReceiver : BroadcastReceiver() {
@@ -22,13 +20,12 @@ class TaskerReceiver : BroadcastReceiver() {
return
} else if (switch) {
if (guid == AppConfig.TASKER_DEFAULT_GUID) {
- Utils.startVServiceFromToggle(context)
+ V2RayServiceManager.startVServiceFromToggle(context)
} else {
- MmkvManager.setSelectServer(guid)
- V2RayServiceManager.startV2Ray(context)
+ V2RayServiceManager.startVService(context, guid)
}
} else {
- Utils.stopVService(context)
+ V2RayServiceManager.stopVService(context)
}
} catch (e: Exception) {
e.printStackTrace()
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/receiver/WidgetProvider.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/receiver/WidgetProvider.kt
index 5aa6c2de..eb9e7854 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/receiver/WidgetProvider.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/receiver/WidgetProvider.kt
@@ -11,7 +11,6 @@ import android.widget.RemoteViews
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
import com.v2ray.ang.service.V2RayServiceManager
-import com.v2ray.ang.util.Utils
class WidgetProvider : AppWidgetProvider() {
/**
@@ -58,9 +57,9 @@ class WidgetProvider : AppWidgetProvider() {
super.onReceive(context, intent)
if (AppConfig.BROADCAST_ACTION_WIDGET_CLICK == intent.action) {
if (V2RayServiceManager.v2rayPoint.isRunning) {
- Utils.stopVService(context)
+ V2RayServiceManager.stopVService(context)
} else {
- Utils.startVServiceFromToggle(context)
+ V2RayServiceManager.startVServiceFromToggle(context)
}
} else if (AppConfig.BROADCAST_ACTION_ACTIVITY == intent.action) {
AppWidgetManager.getInstance(context)?.let { manager ->
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/QSTileService.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/QSTileService.kt
index 151d5c38..aa6da84c 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/QSTileService.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/QSTileService.kt
@@ -64,11 +64,11 @@ class QSTileService : TileService() {
super.onClick()
when (qsTile.state) {
Tile.STATE_INACTIVE -> {
- Utils.startVServiceFromToggle(this)
+ V2RayServiceManager.startVServiceFromToggle(this)
}
Tile.STATE_ACTIVE -> {
- Utils.stopVService(this)
+ V2RayServiceManager.stopVService(this)
}
}
}
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt
index 0de09c2f..62466007 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt
@@ -66,7 +66,28 @@ object V2RayServiceManager {
private var speedNotificationJob: Job? = null
private var mNotificationManager: NotificationManager? = null
- fun startV2Ray(context: Context) {
+ fun startVServiceFromToggle(context: Context): Boolean {
+ if (MmkvManager.getSelectServer().isNullOrEmpty()) {
+ context.toast(R.string.app_tile_first_use)
+ return false
+ }
+ startContextService(context)
+ return true
+ }
+
+ fun startVService(context: Context, guid: String? = null) {
+ if (guid != null) {
+ MmkvManager.setSelectServer(guid)
+ }
+ startContextService(context)
+ }
+
+ fun stopVService(context: Context) {
+ context.toast(R.string.toast_services_stop)
+ MessageUtil.sendMsg2Service(context, AppConfig.MSG_STATE_STOP, "")
+ }
+
+ private fun startContextService(context: Context) {
if (v2rayPoint.isRunning) return
val guid = MmkvManager.getSelectServer() ?: return
val config = MmkvManager.decodeServerConfig(guid) ?: return
@@ -234,7 +255,7 @@ object V2RayServiceManager {
Log.d(ANG_PACKAGE, "Restart Service")
serviceControl.stopService()
Thread.sleep(500L)
- startV2Ray(serviceControl.getService())
+ startVService(serviceControl.getService())
}
AppConfig.MSG_MEASURE_DELAY -> {
@@ -449,7 +470,7 @@ object V2RayServiceManager {
private fun stopSpeedNotification() {
speedNotificationJob?.let {
- it.cancel()
+ it.cancel()
speedNotificationJob = null
updateNotification(currentConfig?.remarks, 0, 0)
}
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt
index f9fdce8e..6d441abf 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt
@@ -140,7 +140,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
binding.fab.setOnClickListener {
if (mainViewModel.isRunning.value == true) {
- Utils.stopVService(this)
+ V2RayServiceManager.stopVService(this)
} else if ((MmkvManager.decodeSettingsString(AppConfig.PREF_MODE) ?: VPN) == VPN) {
val intent = VpnService.prepare(this)
if (intent == null) {
@@ -269,12 +269,12 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
toast(R.string.title_file_chooser)
return
}
- V2RayServiceManager.startV2Ray(this)
+ V2RayServiceManager.startVService(this)
}
fun restartV2Ray() {
if (mainViewModel.isRunning.value == true) {
- Utils.stopVService(this)
+ V2RayServiceManager.stopVService(this)
}
lifecycleScope.launch {
delay(500)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainRecyclerAdapter.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainRecyclerAdapter.kt
index 9801cd90..54cdecf8 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainRecyclerAdapter.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainRecyclerAdapter.kt
@@ -26,7 +26,6 @@ import com.v2ray.ang.service.V2RayServiceManager
import com.v2ray.ang.util.Utils
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
-import java.util.*
class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter(), ItemTouchHelperAdapter {
companion object {
@@ -165,11 +164,11 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter 0) {
addr = addr.drop(1)
@@ -225,7 +225,10 @@ object Utils {
}
private fun isCoreDNSAddress(s: String): Boolean {
- return s.startsWith("https") || s.startsWith("tcp") || s.startsWith("quic") || s == "localhost"
+ return s.startsWith("https")
+ || s.startsWith("tcp")
+ || s.startsWith("quic")
+ || s == "localhost"
}
/**
@@ -249,25 +252,9 @@ object Utils {
return false
}
- fun startVServiceFromToggle(context: Context): Boolean {
- if (MmkvManager.getSelectServer().isNullOrEmpty()) {
- context.toast(R.string.app_tile_first_use)
- return false
- }
- V2RayServiceManager.startV2Ray(context)
- return true
- }
-
- /**
- * stopVService
- */
- fun stopVService(context: Context) {
- context.toast(R.string.toast_services_stop)
- MessageUtil.sendMsg2Service(context, AppConfig.MSG_STATE_STOP, "")
- }
fun openUri(context: Context, uriString: String) {
- val uri = Uri.parse(uriString)
+ val uri = uriString.toUri()
context.startActivity(Intent(Intent.ACTION_VIEW, uri))
}
From 00aed90f2f755e20a357c24a4a1f4c164711b1d8 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Tue, 18 Mar 2025 17:21:42 +0800
Subject: [PATCH 055/238] Add NotificationService, refactor V2RayServiceManager
and V2RayVpnService
---
.../v2ray/ang/service/NotificationService.kt | 215 ++++++++++++++++++
.../v2ray/ang/service/V2RayServiceManager.kt | 208 +----------------
.../com/v2ray/ang/service/V2RayVpnService.kt | 72 +++---
3 files changed, 263 insertions(+), 232 deletions(-)
create mode 100644 V2rayNG/app/src/main/java/com/v2ray/ang/service/NotificationService.kt
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/NotificationService.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/NotificationService.kt
new file mode 100644
index 00000000..3d0178f2
--- /dev/null
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/NotificationService.kt
@@ -0,0 +1,215 @@
+package com.v2ray.ang.service
+
+import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.PendingIntent
+import android.app.Service
+import android.content.Context
+import android.content.Intent
+import android.graphics.Color
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.core.app.NotificationCompat
+import com.v2ray.ang.AppConfig
+import com.v2ray.ang.AppConfig.ANG_PACKAGE
+import com.v2ray.ang.AppConfig.TAG_DIRECT
+import com.v2ray.ang.R
+import com.v2ray.ang.dto.ProfileItem
+import com.v2ray.ang.extension.toSpeedString
+import com.v2ray.ang.handler.MmkvManager
+import com.v2ray.ang.ui.MainActivity
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.isActive
+import kotlinx.coroutines.launch
+import libv2ray.V2RayPoint
+import kotlin.math.min
+
+object NotificationService {
+ private const val NOTIFICATION_ID = 1
+ private const val NOTIFICATION_PENDING_INTENT_CONTENT = 0
+ private const val NOTIFICATION_PENDING_INTENT_STOP_V2RAY = 1
+ private const val NOTIFICATION_PENDING_INTENT_RESTART_V2RAY = 2
+ private const val NOTIFICATION_ICON_THRESHOLD = 3000
+
+ private var lastQueryTime = 0L
+ private var mBuilder: NotificationCompat.Builder? = null
+ private var speedNotificationJob: Job? = null
+ private var mNotificationManager: NotificationManager? = null
+
+
+ fun startSpeedNotification(currentConfig: ProfileItem?, v2rayPoint: V2RayPoint) {
+ if (MmkvManager.decodeSettingsBool(AppConfig.PREF_SPEED_ENABLED) != true) return
+ if (speedNotificationJob != null || v2rayPoint.isRunning == false) return
+
+ lastQueryTime = System.currentTimeMillis()
+ var lastZeroSpeed = false
+ val outboundTags = currentConfig?.getAllOutboundTags()
+ outboundTags?.remove(TAG_DIRECT)
+
+ speedNotificationJob = CoroutineScope(Dispatchers.IO).launch {
+ while (isActive) {
+ val queryTime = System.currentTimeMillis()
+ val sinceLastQueryInSeconds = (queryTime - lastQueryTime) / 1000.0
+ var proxyTotal = 0L
+ val text = StringBuilder()
+ outboundTags?.forEach {
+ val up = v2rayPoint.queryStats(it, AppConfig.UPLINK)
+ val down = v2rayPoint.queryStats(it, AppConfig.DOWNLINK)
+ if (up + down > 0) {
+ appendSpeedString(text, it, up / sinceLastQueryInSeconds, down / sinceLastQueryInSeconds)
+ proxyTotal += up + down
+ }
+ }
+ val directUplink = v2rayPoint.queryStats(TAG_DIRECT, AppConfig.UPLINK)
+ val directDownlink = v2rayPoint.queryStats(TAG_DIRECT, AppConfig.DOWNLINK)
+ val zeroSpeed = proxyTotal == 0L && directUplink == 0L && directDownlink == 0L
+ if (!zeroSpeed || !lastZeroSpeed) {
+ if (proxyTotal == 0L) {
+ appendSpeedString(text, outboundTags?.firstOrNull(), 0.0, 0.0)
+ }
+ appendSpeedString(
+ text, TAG_DIRECT, directUplink / sinceLastQueryInSeconds,
+ directDownlink / sinceLastQueryInSeconds
+ )
+ updateNotification(text.toString(), proxyTotal, directDownlink + directUplink)
+ }
+ lastZeroSpeed = zeroSpeed
+ lastQueryTime = queryTime
+ delay(3000)
+ }
+ }
+ }
+
+ fun showNotification(currentConfig: ProfileItem?) {
+ val service = getService() ?: return
+ val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
+ } else {
+ PendingIntent.FLAG_UPDATE_CURRENT
+ }
+
+ val startMainIntent = Intent(service, MainActivity::class.java)
+ val contentPendingIntent = PendingIntent.getActivity(service, NOTIFICATION_PENDING_INTENT_CONTENT, startMainIntent, flags)
+
+ val stopV2RayIntent = Intent(AppConfig.BROADCAST_ACTION_SERVICE)
+ stopV2RayIntent.`package` = ANG_PACKAGE
+ stopV2RayIntent.putExtra("key", AppConfig.MSG_STATE_STOP)
+ val stopV2RayPendingIntent = PendingIntent.getBroadcast(service, NOTIFICATION_PENDING_INTENT_STOP_V2RAY, stopV2RayIntent, flags)
+
+ val restartV2RayIntent = Intent(AppConfig.BROADCAST_ACTION_SERVICE)
+ restartV2RayIntent.`package` = ANG_PACKAGE
+ restartV2RayIntent.putExtra("key", AppConfig.MSG_STATE_RESTART)
+ val restartV2RayPendingIntent = PendingIntent.getBroadcast(service, NOTIFICATION_PENDING_INTENT_RESTART_V2RAY, restartV2RayIntent, flags)
+
+ val channelId =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ createNotificationChannel()
+ } else {
+ // If earlier version channel ID is not used
+ // https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
+ ""
+ }
+
+ mBuilder = NotificationCompat.Builder(service, channelId)
+ .setSmallIcon(R.drawable.ic_stat_name)
+ .setContentTitle(currentConfig?.remarks)
+ .setPriority(NotificationCompat.PRIORITY_MIN)
+ .setOngoing(true)
+ .setShowWhen(false)
+ .setOnlyAlertOnce(true)
+ .setContentIntent(contentPendingIntent)
+ .addAction(
+ R.drawable.ic_delete_24dp,
+ service.getString(R.string.notification_action_stop_v2ray),
+ stopV2RayPendingIntent
+ )
+ .addAction(
+ R.drawable.ic_delete_24dp,
+ service.getString(R.string.title_service_restart),
+ restartV2RayPendingIntent
+ )
+ //.build()
+
+ //mBuilder?.setDefaults(NotificationCompat.FLAG_ONLY_ALERT_ONCE) //取消震动,铃声其他都不好使
+
+ service.startForeground(NOTIFICATION_ID, mBuilder?.build())
+ }
+
+ fun cancelNotification() {
+ val service = getService() ?: return
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ service.stopForeground(Service.STOP_FOREGROUND_REMOVE)
+ } else {
+ service.stopForeground(true)
+ }
+
+ mBuilder = null
+ speedNotificationJob?.cancel()
+ speedNotificationJob = null
+ }
+
+ fun stopSpeedNotification(currentConfig: ProfileItem?) {
+ speedNotificationJob?.let {
+ it.cancel()
+ speedNotificationJob = null
+ updateNotification(currentConfig?.remarks, 0, 0)
+ }
+ }
+
+ @RequiresApi(Build.VERSION_CODES.O)
+ private fun createNotificationChannel(): String {
+ val channelId = AppConfig.RAY_NG_CHANNEL_ID
+ val channelName = AppConfig.RAY_NG_CHANNEL_NAME
+ val chan = NotificationChannel(
+ channelId,
+ channelName, NotificationManager.IMPORTANCE_HIGH
+ )
+ chan.lightColor = Color.DKGRAY
+ chan.importance = NotificationManager.IMPORTANCE_NONE
+ chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
+ getNotificationManager()?.createNotificationChannel(chan)
+ return channelId
+ }
+
+ private fun updateNotification(contentText: String?, proxyTraffic: Long, directTraffic: Long) {
+ if (mBuilder != null) {
+ if (proxyTraffic < NOTIFICATION_ICON_THRESHOLD && directTraffic < NOTIFICATION_ICON_THRESHOLD) {
+ mBuilder?.setSmallIcon(R.drawable.ic_stat_name)
+ } else if (proxyTraffic > directTraffic) {
+ mBuilder?.setSmallIcon(R.drawable.ic_stat_proxy)
+ } else {
+ mBuilder?.setSmallIcon(R.drawable.ic_stat_direct)
+ }
+ mBuilder?.setStyle(NotificationCompat.BigTextStyle().bigText(contentText))
+ mBuilder?.setContentText(contentText) // Emui4.1 need content text even if style is set as BigTextStyle
+ getNotificationManager()?.notify(NOTIFICATION_ID, mBuilder?.build())
+ }
+ }
+
+ private fun getNotificationManager(): NotificationManager? {
+ if (mNotificationManager == null) {
+ val service = getService() ?: return null
+ mNotificationManager = service.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+ }
+ return mNotificationManager
+ }
+
+ private fun appendSpeedString(text: StringBuilder, name: String?, up: Double, down: Double) {
+ var n = name ?: "no tag"
+ n = n.substring(0, min(n.length, 6))
+ text.append(n)
+ for (i in n.length..6 step 2) {
+ text.append("\t")
+ }
+ text.append("• ${up.toLong().toSpeedString()}↑ ${down.toLong().toSpeedString()}↓\n")
+ }
+
+ private fun getService(): Service? {
+ return V2RayServiceManager.serviceControl?.get()?.getService()
+ }
+
+}
\ No newline at end of file
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt
index 62466007..29c35a59 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt
@@ -1,54 +1,33 @@
package com.v2ray.ang.service
-import android.app.Notification
-import android.app.NotificationChannel
-import android.app.NotificationManager
-import android.app.PendingIntent
-import android.app.Service
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
-import android.graphics.Color
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
-import com.v2ray.ang.AppConfig.VPN
import com.v2ray.ang.R
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.dto.ProfileItem
-import com.v2ray.ang.extension.toSpeedString
import com.v2ray.ang.extension.toast
import com.v2ray.ang.handler.MmkvManager
import com.v2ray.ang.handler.V2rayConfigManager
-import com.v2ray.ang.ui.MainActivity
import com.v2ray.ang.util.MessageUtil
import com.v2ray.ang.util.PluginUtil
import com.v2ray.ang.util.Utils
import go.Seq
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import libv2ray.Libv2ray
import libv2ray.V2RayPoint
import libv2ray.V2RayVPNServiceSupportsSet
import java.lang.ref.SoftReference
-import kotlin.math.min
object V2RayServiceManager {
- private const val NOTIFICATION_ID = 1
- private const val NOTIFICATION_PENDING_INTENT_CONTENT = 0
- private const val NOTIFICATION_PENDING_INTENT_STOP_V2RAY = 1
- private const val NOTIFICATION_PENDING_INTENT_RESTART_V2RAY = 2
- private const val NOTIFICATION_ICON_THRESHOLD = 3000
val v2rayPoint: V2RayPoint = Libv2ray.newV2RayPoint(V2RayCallback(), Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1)
private val mMsgReceive = ReceiveMessageHandler()
@@ -61,11 +40,6 @@ object V2RayServiceManager {
}
var currentConfig: ProfileItem? = null
- private var lastQueryTime = 0L
- private var mBuilder: NotificationCompat.Builder? = null
- private var speedNotificationJob: Job? = null
- private var mNotificationManager: NotificationManager? = null
-
fun startVServiceFromToggle(context: Context): Boolean {
if (MmkvManager.getSelectServer().isNullOrEmpty()) {
context.toast(R.string.app_tile_first_use)
@@ -103,7 +77,7 @@ object V2RayServiceManager {
} else {
context.toast(R.string.toast_services_start)
}
- val intent = if ((MmkvManager.decodeSettingsString(AppConfig.PREF_MODE) ?: VPN) == VPN) {
+ val intent = if ((MmkvManager.decodeSettingsString(AppConfig.PREF_MODE) ?: AppConfig.VPN) == AppConfig.VPN) {
Intent(context.applicationContext, V2RayVpnService::class.java)
} else {
Intent(context.applicationContext, V2RayProxyOnlyService::class.java)
@@ -145,8 +119,7 @@ object V2RayServiceManager {
val serviceControl = serviceControl?.get() ?: return -1
return try {
serviceControl.startService()
- lastQueryTime = System.currentTimeMillis()
- startSpeedNotification()
+ NotificationService.startSpeedNotification(currentConfig, v2rayPoint)
0
} catch (e: Exception) {
Log.d(ANG_PACKAGE, e.toString())
@@ -193,12 +166,12 @@ object V2RayServiceManager {
if (v2rayPoint.isRunning) {
MessageUtil.sendMsg2UI(service, AppConfig.MSG_STATE_START_SUCCESS, "")
- showNotification()
+ NotificationService.showNotification(currentConfig)
PluginUtil.runPlugin(service, config, result.domainPort)
} else {
MessageUtil.sendMsg2UI(service, AppConfig.MSG_STATE_START_FAILURE, "")
- cancelNotification()
+ NotificationService.cancelNotification()
}
}
@@ -216,7 +189,7 @@ object V2RayServiceManager {
}
MessageUtil.sendMsg2UI(service, AppConfig.MSG_STATE_STOP_SUCCESS, "")
- cancelNotification()
+ NotificationService.cancelNotification()
try {
service.unregisterReceiver(mMsgReceive)
@@ -266,12 +239,12 @@ object V2RayServiceManager {
when (intent?.action) {
Intent.ACTION_SCREEN_OFF -> {
Log.d(ANG_PACKAGE, "SCREEN_OFF, stop querying stats")
- stopSpeedNotification()
+ NotificationService.stopSpeedNotification(currentConfig)
}
Intent.ACTION_SCREEN_ON -> {
Log.d(ANG_PACKAGE, "SCREEN_ON, start querying stats")
- startSpeedNotification()
+ NotificationService.startSpeedNotification(currentConfig, v2rayPoint)
}
}
}
@@ -308,171 +281,4 @@ object V2RayServiceManager {
}
}
- private fun showNotification() {
- val service = serviceControl?.get()?.getService() ?: return
- val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
- } else {
- PendingIntent.FLAG_UPDATE_CURRENT
- }
-
- val startMainIntent = Intent(service, MainActivity::class.java)
- val contentPendingIntent = PendingIntent.getActivity(service, NOTIFICATION_PENDING_INTENT_CONTENT, startMainIntent, flags)
-
- val stopV2RayIntent = Intent(AppConfig.BROADCAST_ACTION_SERVICE)
- stopV2RayIntent.`package` = ANG_PACKAGE
- stopV2RayIntent.putExtra("key", AppConfig.MSG_STATE_STOP)
- val stopV2RayPendingIntent = PendingIntent.getBroadcast(service, NOTIFICATION_PENDING_INTENT_STOP_V2RAY, stopV2RayIntent, flags)
-
- val restartV2RayIntent = Intent(AppConfig.BROADCAST_ACTION_SERVICE)
- restartV2RayIntent.`package` = ANG_PACKAGE
- restartV2RayIntent.putExtra("key", AppConfig.MSG_STATE_RESTART)
- val restartV2RayPendingIntent = PendingIntent.getBroadcast(service, NOTIFICATION_PENDING_INTENT_RESTART_V2RAY, restartV2RayIntent, flags)
-
- val channelId =
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- createNotificationChannel()
- } else {
- // If earlier version channel ID is not used
- // https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
- ""
- }
-
- mBuilder = NotificationCompat.Builder(service, channelId)
- .setSmallIcon(R.drawable.ic_stat_name)
- .setContentTitle(currentConfig?.remarks)
- .setPriority(NotificationCompat.PRIORITY_MIN)
- .setOngoing(true)
- .setShowWhen(false)
- .setOnlyAlertOnce(true)
- .setContentIntent(contentPendingIntent)
- .addAction(
- R.drawable.ic_delete_24dp,
- service.getString(R.string.notification_action_stop_v2ray),
- stopV2RayPendingIntent
- )
- .addAction(
- R.drawable.ic_delete_24dp,
- service.getString(R.string.title_service_restart),
- restartV2RayPendingIntent
- )
- //.build()
-
- //mBuilder?.setDefaults(NotificationCompat.FLAG_ONLY_ALERT_ONCE) //取消震动,铃声其他都不好使
-
- service.startForeground(NOTIFICATION_ID, mBuilder?.build())
- }
-
- @RequiresApi(Build.VERSION_CODES.O)
- private fun createNotificationChannel(): String {
- val channelId = AppConfig.RAY_NG_CHANNEL_ID
- val channelName = AppConfig.RAY_NG_CHANNEL_NAME
- val chan = NotificationChannel(
- channelId,
- channelName, NotificationManager.IMPORTANCE_HIGH
- )
- chan.lightColor = Color.DKGRAY
- chan.importance = NotificationManager.IMPORTANCE_NONE
- chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
- getNotificationManager()?.createNotificationChannel(chan)
- return channelId
- }
-
- fun cancelNotification() {
- val service = serviceControl?.get()?.getService() ?: return
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- service.stopForeground(Service.STOP_FOREGROUND_REMOVE)
- } else {
- service.stopForeground(true)
- }
-
- mBuilder = null
- speedNotificationJob?.cancel()
- speedNotificationJob = null
- }
-
- private fun updateNotification(contentText: String?, proxyTraffic: Long, directTraffic: Long) {
- if (mBuilder != null) {
- if (proxyTraffic < NOTIFICATION_ICON_THRESHOLD && directTraffic < NOTIFICATION_ICON_THRESHOLD) {
- mBuilder?.setSmallIcon(R.drawable.ic_stat_name)
- } else if (proxyTraffic > directTraffic) {
- mBuilder?.setSmallIcon(R.drawable.ic_stat_proxy)
- } else {
- mBuilder?.setSmallIcon(R.drawable.ic_stat_direct)
- }
- mBuilder?.setStyle(NotificationCompat.BigTextStyle().bigText(contentText))
- mBuilder?.setContentText(contentText) // Emui4.1 need content text even if style is set as BigTextStyle
- getNotificationManager()?.notify(NOTIFICATION_ID, mBuilder?.build())
- }
- }
-
- private fun getNotificationManager(): NotificationManager? {
- if (mNotificationManager == null) {
- val service = serviceControl?.get()?.getService() ?: return null
- mNotificationManager = service.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
- }
- return mNotificationManager
- }
-
- private fun startSpeedNotification() {
- if (speedNotificationJob == null &&
- v2rayPoint.isRunning &&
- MmkvManager.decodeSettingsBool(AppConfig.PREF_SPEED_ENABLED) == true
- ) {
- var lastZeroSpeed = false
- val outboundTags = currentConfig?.getAllOutboundTags()
- outboundTags?.remove(TAG_DIRECT)
-
- speedNotificationJob = CoroutineScope(Dispatchers.IO).launch {
- while (isActive) {
- val queryTime = System.currentTimeMillis()
- val sinceLastQueryInSeconds = (queryTime - lastQueryTime) / 1000.0
- var proxyTotal = 0L
- val text = StringBuilder()
- outboundTags?.forEach {
- val up = v2rayPoint.queryStats(it, AppConfig.UPLINK)
- val down = v2rayPoint.queryStats(it, AppConfig.DOWNLINK)
- if (up + down > 0) {
- appendSpeedString(text, it, up / sinceLastQueryInSeconds, down / sinceLastQueryInSeconds)
- proxyTotal += up + down
- }
- }
- val directUplink = v2rayPoint.queryStats(TAG_DIRECT, AppConfig.UPLINK)
- val directDownlink = v2rayPoint.queryStats(TAG_DIRECT, AppConfig.DOWNLINK)
- val zeroSpeed = proxyTotal == 0L && directUplink == 0L && directDownlink == 0L
- if (!zeroSpeed || !lastZeroSpeed) {
- if (proxyTotal == 0L) {
- appendSpeedString(text, outboundTags?.firstOrNull(), 0.0, 0.0)
- }
- appendSpeedString(
- text, TAG_DIRECT, directUplink / sinceLastQueryInSeconds,
- directDownlink / sinceLastQueryInSeconds
- )
- updateNotification(text.toString(), proxyTotal, directDownlink + directUplink)
- }
- lastZeroSpeed = zeroSpeed
- lastQueryTime = queryTime
- delay(3000)
- }
- }
- }
- }
-
- private fun appendSpeedString(text: StringBuilder, name: String?, up: Double, down: Double) {
- var n = name ?: "no tag"
- n = n.substring(0, min(n.length, 6))
- text.append(n)
- for (i in n.length..6 step 2) {
- text.append("\t")
- }
- text.append("• ${up.toLong().toSpeedString()}↑ ${down.toLong().toSpeedString()}↓\n")
- }
-
- private fun stopSpeedNotification() {
- speedNotificationJob?.let {
- it.cancel()
- speedNotificationJob = null
- updateNotification(currentConfig?.remarks, 0, 0)
- }
- }
}
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayVpnService.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayVpnService.kt
index 82b57a36..03dcd48f 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayVpnService.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayVpnService.kt
@@ -105,7 +105,37 @@ class V2RayVpnService : VpnService(), ServiceControl {
override fun onDestroy() {
super.onDestroy()
- V2RayServiceManager.cancelNotification()
+ NotificationService.cancelNotification()
+ }
+
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+ V2RayServiceManager.startV2rayPoint()
+ return START_STICKY
+ //return super.onStartCommand(intent, flags, startId)
+ }
+
+ override fun getService(): Service {
+ return this
+ }
+
+ override fun startService() {
+ setup()
+ }
+
+ override fun stopService() {
+ stopV2Ray(true)
+ }
+
+ override fun vpnProtect(socket: Int): Boolean {
+ return protect(socket)
+ }
+
+ @RequiresApi(Build.VERSION_CODES.N)
+ override fun attachBaseContext(newBase: Context?) {
+ val context = newBase?.let {
+ MyContextWrapper.wrap(newBase, Utils.getLocale())
+ }
+ super.attachBaseContext(context)
}
private fun setup() {
@@ -114,6 +144,14 @@ class V2RayVpnService : VpnService(), ServiceControl {
return
}
+ if (setupVpnService() != true) {
+ return
+ }
+
+ runTun2socks()
+ }
+
+ private fun setupVpnService(): Boolean {
// If the old interface has exactly the same parameters, use it!
// Configure a builder while parsing the parameters.
val builder = Builder()
@@ -200,12 +238,13 @@ class V2RayVpnService : VpnService(), ServiceControl {
try {
mInterface = builder.establish()!!
isRunning = true
- runTun2socks()
+ return true
} catch (e: Exception) {
// non-nullable lateinit var
e.printStackTrace()
stopV2Ray()
}
+ return false
}
private fun runTun2socks() {
@@ -279,12 +318,6 @@ class V2RayVpnService : VpnService(), ServiceControl {
}
}
- override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
- V2RayServiceManager.startV2rayPoint()
- return START_STICKY
- //return super.onStartCommand(intent, flags, startId)
- }
-
private fun stopV2Ray(isForced: Boolean = true) {
// val configName = defaultDPreference.getPrefString(PREF_CURR_CONFIG_GUID, "")
// val emptyInfo = VpnNetworkInfo()
@@ -324,27 +357,4 @@ class V2RayVpnService : VpnService(), ServiceControl {
}
}
- override fun getService(): Service {
- return this
- }
-
- override fun startService() {
- setup()
- }
-
- override fun stopService() {
- stopV2Ray(true)
- }
-
- override fun vpnProtect(socket: Int): Boolean {
- return protect(socket)
- }
-
- @RequiresApi(Build.VERSION_CODES.N)
- override fun attachBaseContext(newBase: Context?) {
- val context = newBase?.let {
- MyContextWrapper.wrap(newBase, Utils.getLocale())
- }
- super.attachBaseContext(context)
- }
}
From b52dd331023dc2b692c308f568ed659311d8ad3b Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Tue, 18 Mar 2025 21:00:22 +0800
Subject: [PATCH 056/238] Optimize and improve V2RayServiceManager
---
.../com/v2ray/ang/receiver/WidgetProvider.kt | 4 +-
.../v2ray/ang/service/NotificationService.kt | 13 +-
.../com/v2ray/ang/service/QSTileService.kt | 2 +-
.../v2ray/ang/service/V2RayServiceManager.kt | 164 ++++++++++--------
.../com/v2ray/ang/service/V2RayVpnService.kt | 2 +-
.../java/com/v2ray/ang/ui/ScSwitchActivity.kt | 2 +-
6 files changed, 99 insertions(+), 88 deletions(-)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/receiver/WidgetProvider.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/receiver/WidgetProvider.kt
index eb9e7854..bfe05f60 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/receiver/WidgetProvider.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/receiver/WidgetProvider.kt
@@ -18,7 +18,7 @@ class WidgetProvider : AppWidgetProvider() {
*/
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
super.onUpdate(context, appWidgetManager, appWidgetIds)
- updateWidgetBackground(context, appWidgetManager, appWidgetIds, V2RayServiceManager.v2rayPoint.isRunning)
+ updateWidgetBackground(context, appWidgetManager, appWidgetIds, V2RayServiceManager.isRunning())
}
@@ -56,7 +56,7 @@ class WidgetProvider : AppWidgetProvider() {
override fun onReceive(context: Context, intent: Intent) {
super.onReceive(context, intent)
if (AppConfig.BROADCAST_ACTION_WIDGET_CLICK == intent.action) {
- if (V2RayServiceManager.v2rayPoint.isRunning) {
+ if (V2RayServiceManager.isRunning()) {
V2RayServiceManager.stopVService(context)
} else {
V2RayServiceManager.startVServiceFromToggle(context)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/NotificationService.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/NotificationService.kt
index 3d0178f2..3b9fa40c 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/NotificationService.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/NotificationService.kt
@@ -25,7 +25,6 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
-import libv2ray.V2RayPoint
import kotlin.math.min
object NotificationService {
@@ -41,9 +40,9 @@ object NotificationService {
private var mNotificationManager: NotificationManager? = null
- fun startSpeedNotification(currentConfig: ProfileItem?, v2rayPoint: V2RayPoint) {
+ fun startSpeedNotification(currentConfig: ProfileItem?) {
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_SPEED_ENABLED) != true) return
- if (speedNotificationJob != null || v2rayPoint.isRunning == false) return
+ if (speedNotificationJob != null || V2RayServiceManager.isRunning() == false) return
lastQueryTime = System.currentTimeMillis()
var lastZeroSpeed = false
@@ -57,15 +56,15 @@ object NotificationService {
var proxyTotal = 0L
val text = StringBuilder()
outboundTags?.forEach {
- val up = v2rayPoint.queryStats(it, AppConfig.UPLINK)
- val down = v2rayPoint.queryStats(it, AppConfig.DOWNLINK)
+ val up = V2RayServiceManager.queryStats(it, AppConfig.UPLINK)
+ val down = V2RayServiceManager.queryStats(it, AppConfig.DOWNLINK)
if (up + down > 0) {
appendSpeedString(text, it, up / sinceLastQueryInSeconds, down / sinceLastQueryInSeconds)
proxyTotal += up + down
}
}
- val directUplink = v2rayPoint.queryStats(TAG_DIRECT, AppConfig.UPLINK)
- val directDownlink = v2rayPoint.queryStats(TAG_DIRECT, AppConfig.DOWNLINK)
+ val directUplink = V2RayServiceManager.queryStats(TAG_DIRECT, AppConfig.UPLINK)
+ val directDownlink = V2RayServiceManager.queryStats(TAG_DIRECT, AppConfig.DOWNLINK)
val zeroSpeed = proxyTotal == 0L && directUplink == 0L && directDownlink == 0L
if (!zeroSpeed || !lastZeroSpeed) {
if (proxyTotal == 0L) {
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/QSTileService.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/QSTileService.kt
index aa6da84c..e5d0dec5 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/QSTileService.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/QSTileService.kt
@@ -26,7 +26,7 @@ class QSTileService : TileService() {
qsTile?.icon = Icon.createWithResource(applicationContext, R.drawable.ic_stat_name)
} else if (state == Tile.STATE_ACTIVE) {
qsTile?.state = Tile.STATE_ACTIVE
- qsTile?.label = V2RayServiceManager.currentConfig?.remarks
+ qsTile?.label = V2RayServiceManager.getRunningServerName()
qsTile?.icon = Icon.createWithResource(applicationContext, R.drawable.ic_stat_name)
}
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt
index 29c35a59..8e273692 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt
@@ -1,5 +1,6 @@
package com.v2ray.ang.service
+import android.app.Service
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
@@ -29,8 +30,9 @@ import java.lang.ref.SoftReference
object V2RayServiceManager {
- val v2rayPoint: V2RayPoint = Libv2ray.newV2RayPoint(V2RayCallback(), Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1)
+ private val v2rayPoint: V2RayPoint = Libv2ray.newV2RayPoint(V2RayCallback(), Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1)
private val mMsgReceive = ReceiveMessageHandler()
+ private var currentConfig: ProfileItem? = null
var serviceControl: SoftReference? = null
set(value) {
@@ -38,7 +40,6 @@ object V2RayServiceManager {
Seq.setContext(value?.get()?.getService()?.applicationContext)
Libv2ray.initV2Env(Utils.userAssetPath(value?.get()?.getService()), Utils.getDeviceIdForXUDPBaseKey())
}
- var currentConfig: ProfileItem? = null
fun startVServiceFromToggle(context: Context): Boolean {
if (MmkvManager.getSelectServer().isNullOrEmpty()) {
@@ -61,6 +62,10 @@ object V2RayServiceManager {
MessageUtil.sendMsg2Service(context, AppConfig.MSG_STATE_STOP, "")
}
+ fun isRunning() = v2rayPoint.isRunning
+
+ fun getRunningServerName() = currentConfig?.remarks.orEmpty()
+
private fun startContextService(context: Context) {
if (v2rayPoint.isRunning) return
val guid = MmkvManager.getSelectServer() ?: return
@@ -89,52 +94,13 @@ object V2RayServiceManager {
}
}
- private class V2RayCallback : V2RayVPNServiceSupportsSet {
- override fun shutdown(): Long {
- val serviceControl = serviceControl?.get() ?: return -1
- // called by go
- return try {
- serviceControl.stopService()
- 0
- } catch (e: Exception) {
- Log.d(ANG_PACKAGE, e.toString())
- -1
- }
- }
-
- override fun prepare(): Long {
- return 0
- }
-
- override fun protect(l: Long): Boolean {
- val serviceControl = serviceControl?.get() ?: return true
- return serviceControl.vpnProtect(l.toInt())
- }
-
- override fun onEmitStatus(l: Long, s: String?): Long {
- return 0
- }
-
- override fun setup(s: String): Long {
- val serviceControl = serviceControl?.get() ?: return -1
- return try {
- serviceControl.startService()
- NotificationService.startSpeedNotification(currentConfig, v2rayPoint)
- 0
- } catch (e: Exception) {
- Log.d(ANG_PACKAGE, e.toString())
- -1
- }
- }
- }
-
/**
* 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 service = getService() ?: return
val guid = MmkvManager.getSelectServer() ?: return
val config = MmkvManager.decodeServerConfig(guid) ?: return
if (v2rayPoint.isRunning) {
@@ -176,7 +142,7 @@ object V2RayServiceManager {
}
fun stopV2rayPoint() {
- val service = serviceControl?.get()?.getService() ?: return
+ val service = getService() ?: return
if (v2rayPoint.isRunning) {
CoroutineScope(Dispatchers.IO).launch {
@@ -199,6 +165,84 @@ object V2RayServiceManager {
PluginUtil.stopPlugin()
}
+ fun queryStats(tag: String, link: String): Long {
+ return v2rayPoint.queryStats(tag, link)
+ }
+
+ private fun measureV2rayDelay() {
+ CoroutineScope(Dispatchers.IO).launch {
+ val service = getService() ?: return@launch
+ var time = -1L
+ var errstr = ""
+ if (v2rayPoint.isRunning) {
+ try {
+ time = v2rayPoint.measureDelay(Utils.getDelayTestUrl())
+ } catch (e: Exception) {
+ Log.d(ANG_PACKAGE, "measureV2rayDelay: $e")
+ errstr = e.message?.substringAfter("\":") ?: "empty message"
+ }
+ if (time == -1L) {
+ try {
+ time = v2rayPoint.measureDelay(Utils.getDelayTestUrl(true))
+ } catch (e: Exception) {
+ Log.d(ANG_PACKAGE, "measureV2rayDelay: $e")
+ errstr = e.message?.substringAfter("\":") ?: "empty message"
+ }
+ }
+ }
+ val result = if (time == -1L) {
+ service.getString(R.string.connection_test_error, errstr)
+ } else {
+ service.getString(R.string.connection_test_available, time)
+ }
+
+ MessageUtil.sendMsg2UI(service, AppConfig.MSG_MEASURE_DELAY_SUCCESS, result)
+ }
+ }
+
+ private fun getService(): Service? {
+ return serviceControl?.get()?.getService()
+ }
+
+ private class V2RayCallback : V2RayVPNServiceSupportsSet {
+ override fun shutdown(): Long {
+ val serviceControl = serviceControl?.get() ?: return -1
+ // called by go
+ return try {
+ serviceControl.stopService()
+ 0
+ } catch (e: Exception) {
+ Log.d(ANG_PACKAGE, e.toString())
+ -1
+ }
+ }
+
+ override fun prepare(): Long {
+ return 0
+ }
+
+ override fun protect(l: Long): Boolean {
+ val serviceControl = serviceControl?.get() ?: return true
+ return serviceControl.vpnProtect(l.toInt())
+ }
+
+ override fun onEmitStatus(l: Long, s: String?): Long {
+ return 0
+ }
+
+ override fun setup(s: String): Long {
+ val serviceControl = serviceControl?.get() ?: return -1
+ return try {
+ serviceControl.startService()
+ NotificationService.startSpeedNotification(currentConfig)
+ 0
+ } catch (e: Exception) {
+ Log.d(ANG_PACKAGE, e.toString())
+ -1
+ }
+ }
+ }
+
private class ReceiveMessageHandler : BroadcastReceiver() {
override fun onReceive(ctx: Context?, intent: Intent?) {
val serviceControl = serviceControl?.get() ?: return
@@ -244,41 +288,9 @@ object V2RayServiceManager {
Intent.ACTION_SCREEN_ON -> {
Log.d(ANG_PACKAGE, "SCREEN_ON, start querying stats")
- NotificationService.startSpeedNotification(currentConfig, v2rayPoint)
+ NotificationService.startSpeedNotification(currentConfig)
}
}
}
}
-
- private fun measureV2rayDelay() {
- CoroutineScope(Dispatchers.IO).launch {
- val service = serviceControl?.get()?.getService() ?: return@launch
- var time = -1L
- var errstr = ""
- if (v2rayPoint.isRunning) {
- try {
- time = v2rayPoint.measureDelay(Utils.getDelayTestUrl())
- } catch (e: Exception) {
- Log.d(ANG_PACKAGE, "measureV2rayDelay: $e")
- errstr = e.message?.substringAfter("\":") ?: "empty message"
- }
- if (time == -1L) {
- try {
- time = v2rayPoint.measureDelay(Utils.getDelayTestUrl(true))
- } catch (e: Exception) {
- Log.d(ANG_PACKAGE, "measureV2rayDelay: $e")
- errstr = e.message?.substringAfter("\":") ?: "empty message"
- }
- }
- }
- val result = if (time == -1L) {
- service.getString(R.string.connection_test_error, errstr)
- } else {
- service.getString(R.string.connection_test_available, time)
- }
-
- MessageUtil.sendMsg2UI(service, AppConfig.MSG_MEASURE_DELAY_SUCCESS, result)
- }
- }
-
}
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayVpnService.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayVpnService.kt
index 03dcd48f..57e36ce0 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayVpnService.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayVpnService.kt
@@ -190,7 +190,7 @@ class V2RayVpnService : VpnService(), ServiceControl {
}
// }
- builder.setSession(V2RayServiceManager.currentConfig?.remarks.orEmpty())
+ builder.setSession(V2RayServiceManager.getRunningServerName())
val selfPackageName = BuildConfig.APPLICATION_ID
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_PER_APP_PROXY)) {
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ScSwitchActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ScSwitchActivity.kt
index 3f5ce17c..0495318a 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ScSwitchActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ScSwitchActivity.kt
@@ -11,7 +11,7 @@ class ScSwitchActivity : BaseActivity() {
setContentView(R.layout.activity_none)
- if (V2RayServiceManager.v2rayPoint.isRunning) {
+ if (V2RayServiceManager.isRunning()) {
V2RayServiceManager.stopVService(this)
} else {
V2RayServiceManager.startVServiceFromToggle(this)
From 0165ad54b3920c53721421fa5fb922577afe85e8 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Tue, 18 Mar 2025 21:04:51 +0800
Subject: [PATCH 057/238] Update build.gradle.kts
---
V2rayNG/app/build.gradle.kts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/V2rayNG/app/build.gradle.kts b/V2rayNG/app/build.gradle.kts
index 9e4f169e..b650fa26 100644
--- a/V2rayNG/app/build.gradle.kts
+++ b/V2rayNG/app/build.gradle.kts
@@ -12,7 +12,7 @@ android {
applicationId = "com.v2ray.ang"
minSdk = 21
targetSdk = 35
- versionCode = 636
+ versionCode = 638
versionName = "1.9.39"
multiDexEnabled = true
From 093716baaa1b4d3902fd43a1ebd9b39850ae69ae Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Wed, 19 Mar 2025 10:38:58 +0800
Subject: [PATCH 058/238] Optimize and improve SettingsManager
---
.../main/java/com/v2ray/ang/AngApplication.kt | 3 +-
.../com/v2ray/ang/handler/SettingsManager.kt | 77 ++++++++++++++++++-
.../v2ray/ang/handler/V2rayConfigManager.kt | 6 +-
.../ang/service/V2RayProxyOnlyService.kt | 4 +-
.../v2ray/ang/service/V2RayServiceManager.kt | 5 +-
.../com/v2ray/ang/service/V2RayVpnService.kt | 4 +-
.../java/com/v2ray/ang/ui/BaseActivity.kt | 3 +-
.../java/com/v2ray/ang/util/SpeedtestUtil.kt | 5 +-
.../src/main/java/com/v2ray/ang/util/Utils.kt | 75 +-----------------
.../v2ray/ang/viewmodel/SettingsViewModel.kt | 4 +-
10 files changed, 95 insertions(+), 91 deletions(-)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/AngApplication.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/AngApplication.kt
index a999a9b4..13a31b2b 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/AngApplication.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/AngApplication.kt
@@ -7,7 +7,6 @@ import androidx.work.WorkManager
import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig.ANG_PACKAGE
import com.v2ray.ang.handler.SettingsManager
-import com.v2ray.ang.util.Utils
class AngApplication : MultiDexApplication() {
companion object {
@@ -36,7 +35,7 @@ class AngApplication : MultiDexApplication() {
MMKV.initialize(this)
- Utils.setNightMode()
+ SettingsManager.setNightMode()
// Initialize WorkManager with the custom configuration
WorkManager.initialize(this, workManagerConfiguration)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SettingsManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SettingsManager.kt
index cf116a4d..50c88dba 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SettingsManager.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SettingsManager.kt
@@ -4,12 +4,14 @@ import android.content.Context
import android.content.res.AssetManager
import android.text.TextUtils
import android.util.Log
+import androidx.appcompat.app.AppCompatDelegate
import com.v2ray.ang.AppConfig
import com.v2ray.ang.AppConfig.ANG_PACKAGE
import com.v2ray.ang.AppConfig.GEOIP_PRIVATE
import com.v2ray.ang.AppConfig.GEOSITE_PRIVATE
import com.v2ray.ang.AppConfig.TAG_DIRECT
import com.v2ray.ang.dto.EConfigType
+import com.v2ray.ang.dto.Language
import com.v2ray.ang.dto.ProfileItem
import com.v2ray.ang.dto.RoutingType
import com.v2ray.ang.dto.RulesetItem
@@ -18,10 +20,10 @@ import com.v2ray.ang.handler.MmkvManager.decodeServerConfig
import com.v2ray.ang.handler.MmkvManager.decodeServerList
import com.v2ray.ang.util.JsonUtil
import com.v2ray.ang.util.Utils
-import com.v2ray.ang.util.Utils.parseInt
import java.io.File
import java.io.FileOutputStream
import java.util.Collections
+import java.util.Locale
object SettingsManager {
@@ -173,7 +175,7 @@ object SettingsManager {
}
fun getSocksPort(): Int {
- return parseInt(MmkvManager.decodeSettingsString(AppConfig.PREF_SOCKS_PORT), AppConfig.PORT_SOCKS.toInt())
+ return Utils.parseInt(MmkvManager.decodeSettingsString(AppConfig.PREF_SOCKS_PORT), AppConfig.PORT_SOCKS.toInt())
}
fun getHttpPort(): Int {
@@ -205,4 +207,75 @@ object SettingsManager {
}
}
+
+ /**
+ * get domestic dns servers from preference
+ */
+ fun getDomesticDnsServers(): List {
+ val domesticDns =
+ MmkvManager.decodeSettingsString(AppConfig.PREF_DOMESTIC_DNS) ?: AppConfig.DNS_DIRECT
+ val ret = domesticDns.split(",").filter { Utils.isPureIpAddress(it) || Utils.isCoreDNSAddress(it) }
+ if (ret.isEmpty()) {
+ return listOf(AppConfig.DNS_DIRECT)
+ }
+ return ret
+ }
+
+ /**
+ * get remote dns servers from preference
+ */
+ fun getRemoteDnsServers(): List {
+ val remoteDns =
+ MmkvManager.decodeSettingsString(AppConfig.PREF_REMOTE_DNS) ?: AppConfig.DNS_PROXY
+ val ret = remoteDns.split(",").filter { Utils.isPureIpAddress(it) || Utils.isCoreDNSAddress(it) }
+ if (ret.isEmpty()) {
+ return listOf(AppConfig.DNS_PROXY)
+ }
+ return ret
+ }
+
+ /**
+ * get vpn dns servers from preference
+ */
+ fun getVpnDnsServers(): List {
+ val vpnDns = MmkvManager.decodeSettingsString(AppConfig.PREF_VPN_DNS) ?: AppConfig.DNS_VPN
+ return vpnDns.split(",").filter { Utils.isPureIpAddress(it) }
+ // allow empty, in that case dns will use system default
+ }
+
+
+ fun getDelayTestUrl(second: Boolean = false): String {
+ return if (second) {
+ AppConfig.DelayTestUrl2
+ } else {
+ MmkvManager.decodeSettingsString(AppConfig.PREF_DELAY_TEST_URL)
+ ?: AppConfig.DelayTestUrl
+ }
+ }
+
+ fun getLocale(): Locale {
+ val langCode =
+ MmkvManager.decodeSettingsString(AppConfig.PREF_LANGUAGE) ?: Language.AUTO.code
+ val language = Language.fromCode(langCode)
+
+ return when (language) {
+ Language.AUTO -> Utils.getSysLocale()
+ Language.ENGLISH -> Locale.ENGLISH
+ Language.CHINA -> Locale.CHINA
+ Language.TRADITIONAL_CHINESE -> Locale.TRADITIONAL_CHINESE
+ Language.VIETNAMESE -> Locale("vi")
+ Language.RUSSIAN -> Locale("ru")
+ Language.PERSIAN -> Locale("fa")
+ Language.BANGLA -> Locale("bn")
+ Language.BAKHTIARI -> Locale("bqi", "IR")
+ }
+ }
+
+ fun setNightMode() {
+ when (MmkvManager.decodeSettingsString(AppConfig.PREF_UI_MODE_NIGHT, "0")) {
+ "0" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
+ "1" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
+ "2" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
+ }
+ }
}
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt
index 72d81345..65321724 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt
@@ -271,7 +271,7 @@ object V2rayConfigManager {
}
// DNS inbound对象
- val remoteDns = Utils.getRemoteDnsServers()
+ val remoteDns = SettingsManager.getRemoteDnsServers()
if (v2rayConfig.inbounds.none { e -> e.protocol == "dokodemo-door" && e.tag == "dns-in" }) {
val dnsInboundSettings = V2rayConfig.InboundBean.InSettingsBean(
address = if (Utils.isPureIpAddress(remoteDns.first())) remoteDns.first() else AppConfig.DNS_PROXY,
@@ -329,7 +329,7 @@ object V2rayConfigManager {
val servers = ArrayList()
//remote Dns
- val remoteDns = Utils.getRemoteDnsServers()
+ val remoteDns = SettingsManager.getRemoteDnsServers()
val proxyDomain = userRule2Domain(TAG_PROXY)
remoteDns.forEach {
servers.add(it)
@@ -344,7 +344,7 @@ object V2rayConfigManager {
}
// domestic DNS
- val domesticDns = Utils.getDomesticDnsServers()
+ val domesticDns = SettingsManager.getDomesticDnsServers()
val directDomain = userRule2Domain(TAG_DIRECT)
val isCnRoutingMode = directDomain.contains(GEOSITE_CN)
val geoipCn = arrayListOf(GEOIP_CN)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayProxyOnlyService.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayProxyOnlyService.kt
index 4d387a45..589e2c6f 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayProxyOnlyService.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayProxyOnlyService.kt
@@ -6,8 +6,8 @@ import android.content.Intent
import android.os.Build
import android.os.IBinder
import androidx.annotation.RequiresApi
+import com.v2ray.ang.handler.SettingsManager
import com.v2ray.ang.util.MyContextWrapper
-import com.v2ray.ang.util.Utils
import java.lang.ref.SoftReference
class V2RayProxyOnlyService : Service(), ServiceControl {
@@ -49,7 +49,7 @@ class V2RayProxyOnlyService : Service(), ServiceControl {
@RequiresApi(Build.VERSION_CODES.N)
override fun attachBaseContext(newBase: Context?) {
val context = newBase?.let {
- MyContextWrapper.wrap(newBase, Utils.getLocale())
+ MyContextWrapper.wrap(newBase, SettingsManager.getLocale())
}
super.attachBaseContext(context)
}
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt
index 8e273692..3d36d24e 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt
@@ -15,6 +15,7 @@ import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.dto.ProfileItem
import com.v2ray.ang.extension.toast
import com.v2ray.ang.handler.MmkvManager
+import com.v2ray.ang.handler.SettingsManager
import com.v2ray.ang.handler.V2rayConfigManager
import com.v2ray.ang.util.MessageUtil
import com.v2ray.ang.util.PluginUtil
@@ -176,14 +177,14 @@ object V2RayServiceManager {
var errstr = ""
if (v2rayPoint.isRunning) {
try {
- time = v2rayPoint.measureDelay(Utils.getDelayTestUrl())
+ time = v2rayPoint.measureDelay(SettingsManager.getDelayTestUrl())
} catch (e: Exception) {
Log.d(ANG_PACKAGE, "measureV2rayDelay: $e")
errstr = e.message?.substringAfter("\":") ?: "empty message"
}
if (time == -1L) {
try {
- time = v2rayPoint.measureDelay(Utils.getDelayTestUrl(true))
+ time = v2rayPoint.measureDelay(SettingsManager.getDelayTestUrl(true))
} catch (e: Exception) {
Log.d(ANG_PACKAGE, "measureV2rayDelay: $e")
errstr = e.message?.substringAfter("\":") ?: "empty message"
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayVpnService.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayVpnService.kt
index 57e36ce0..11b12668 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayVpnService.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayVpnService.kt
@@ -133,7 +133,7 @@ class V2RayVpnService : VpnService(), ServiceControl {
@RequiresApi(Build.VERSION_CODES.N)
override fun attachBaseContext(newBase: Context?) {
val context = newBase?.let {
- MyContextWrapper.wrap(newBase, Utils.getLocale())
+ MyContextWrapper.wrap(newBase, SettingsManager.getLocale())
}
super.attachBaseContext(context)
}
@@ -182,7 +182,7 @@ class V2RayVpnService : VpnService(), ServiceControl {
// if (MmkvManager.decodeSettingsBool(AppConfig.PREF_LOCAL_DNS_ENABLED) == true) {
// builder.addDnsServer(PRIVATE_VLAN4_ROUTER)
// } else {
- Utils.getVpnDnsServers()
+ SettingsManager.getVpnDnsServers()
.forEach {
if (Utils.isPureIpAddress(it)) {
builder.addDnsServer(it)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/BaseActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/BaseActivity.kt
index f2d4ba94..46287ceb 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/BaseActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/BaseActivity.kt
@@ -7,6 +7,7 @@ import android.view.MenuItem
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.WindowCompat
+import com.v2ray.ang.handler.SettingsManager
import com.v2ray.ang.util.MyContextWrapper
import com.v2ray.ang.util.Utils
@@ -34,6 +35,6 @@ abstract class BaseActivity : AppCompatActivity() {
@RequiresApi(Build.VERSION_CODES.N)
override fun attachBaseContext(newBase: Context?) {
- super.attachBaseContext(MyContextWrapper.wrap(newBase ?: return, Utils.getLocale()))
+ super.attachBaseContext(MyContextWrapper.wrap(newBase ?: return, SettingsManager.getLocale()))
}
}
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/util/SpeedtestUtil.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/util/SpeedtestUtil.kt
index 6aae9bfc..166d9198 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/util/SpeedtestUtil.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/util/SpeedtestUtil.kt
@@ -7,6 +7,7 @@ import android.util.Log
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
import com.v2ray.ang.extension.responseLength
+import com.v2ray.ang.handler.SettingsManager
import kotlinx.coroutines.isActive
import libv2ray.Libv2ray
import java.io.IOException
@@ -35,7 +36,7 @@ object SpeedtestUtil {
fun realPing(config: String): Long {
return try {
- Libv2ray.measureOutboundDelay(config, Utils.getDelayTestUrl())
+ Libv2ray.measureOutboundDelay(config, SettingsManager.getDelayTestUrl())
} catch (e: Exception) {
Log.d(AppConfig.ANG_PACKAGE, "realPing: $e")
-1L
@@ -98,7 +99,7 @@ object SpeedtestUtil {
var result: String
var elapsed = -1L
- val conn = HttpUtil.createProxyConnection(Utils.getDelayTestUrl(), port, 30000, 30000) ?: return Pair(elapsed, "")
+ val conn = HttpUtil.createProxyConnection(SettingsManager.getDelayTestUrl(), port, 30000, 30000) ?: return Pair(elapsed, "")
try {
val start = SystemClock.elapsedRealtime()
val code = conn.responseCode
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/util/Utils.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/util/Utils.kt
index 6f257717..54bdd16f 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/util/Utils.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/util/Utils.kt
@@ -15,14 +15,11 @@ import android.util.Base64
import android.util.Log
import android.util.Patterns
import android.webkit.URLUtil
-import androidx.appcompat.app.AppCompatDelegate
import androidx.core.content.ContextCompat
import androidx.core.net.toUri
import com.v2ray.ang.AppConfig
import com.v2ray.ang.AppConfig.ANG_PACKAGE
import com.v2ray.ang.AppConfig.LOOPBACK
-import com.v2ray.ang.dto.Language
-import com.v2ray.ang.handler.MmkvManager
import java.io.IOException
import java.net.ServerSocket
import java.net.URLDecoder
@@ -126,37 +123,6 @@ object Utils {
}
}
- /**
- * get remote dns servers from preference
- */
- fun getRemoteDnsServers(): List {
- val remoteDns =
- MmkvManager.decodeSettingsString(AppConfig.PREF_REMOTE_DNS) ?: AppConfig.DNS_PROXY
- val ret = remoteDns.split(",").filter { isPureIpAddress(it) || isCoreDNSAddress(it) }
- if (ret.isEmpty()) {
- return listOf(AppConfig.DNS_PROXY)
- }
- return ret
- }
-
- fun getVpnDnsServers(): List {
- val vpnDns = MmkvManager.decodeSettingsString(AppConfig.PREF_VPN_DNS) ?: AppConfig.DNS_VPN
- return vpnDns.split(",").filter { isPureIpAddress(it) }
- // allow empty, in that case dns will use system default
- }
-
- /**
- * get remote dns servers from preference
- */
- fun getDomesticDnsServers(): List {
- val domesticDns =
- MmkvManager.decodeSettingsString(AppConfig.PREF_DOMESTIC_DNS) ?: AppConfig.DNS_DIRECT
- val ret = domesticDns.split(",").filter { isPureIpAddress(it) || isCoreDNSAddress(it) }
- if (ret.isEmpty()) {
- return listOf(AppConfig.DNS_DIRECT)
- }
- return ret
- }
/**
* is ip address
@@ -224,7 +190,7 @@ object Utils {
return regV6.matches(addr)
}
- private fun isCoreDNSAddress(s: String): Boolean {
+ fun isCoreDNSAddress(s: String): Boolean {
return s.startsWith("https")
|| s.startsWith("tcp")
|| s.startsWith("quic")
@@ -327,15 +293,6 @@ object Utils {
return context.resources.configuration.uiMode and UI_MODE_NIGHT_MASK != UI_MODE_NIGHT_NO
}
-
- fun setNightMode() {
- when (MmkvManager.decodeSettingsString(AppConfig.PREF_UI_MODE_NIGHT, "0")) {
- "0" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
- "1" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
- "2" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
- }
- }
-
fun getIpv6Address(address: String?): String {
if (address == null) {
return ""
@@ -347,26 +304,7 @@ object Utils {
}
}
- fun getLocale(): Locale {
- val langCode =
- MmkvManager.decodeSettingsString(AppConfig.PREF_LANGUAGE) ?: Language.AUTO.code
- val language = Language.fromCode(langCode)
-
- return when (language) {
- Language.AUTO -> getSysLocale()
- Language.ENGLISH -> Locale.ENGLISH
- Language.CHINA -> Locale.CHINA
- Language.TRADITIONAL_CHINESE -> Locale.TRADITIONAL_CHINESE
- Language.VIETNAMESE -> Locale("vi")
- Language.RUSSIAN -> Locale("ru")
- Language.PERSIAN -> Locale("fa")
- Language.BANGLA -> Locale("bn")
- Language.BAKHTIARI -> Locale("bqi", "IR")
- }
- }
-
-
- private fun getSysLocale(): Locale = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ fun getSysLocale(): Locale = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
LocaleList.getDefault()[0]
} else {
Locale.getDefault()
@@ -385,15 +323,6 @@ object Utils {
fun isTv(context: Context): Boolean =
context.packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
- fun getDelayTestUrl(second: Boolean = false): String {
- return if (second) {
- AppConfig.DelayTestUrl2
- } else {
- MmkvManager.decodeSettingsString(AppConfig.PREF_DELAY_TEST_URL)
- ?: AppConfig.DelayTestUrl
- }
- }
-
fun findFreePort(ports: List): Int {
for (port in ports) {
try {
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/SettingsViewModel.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/SettingsViewModel.kt
index 9e73ff39..96c92225 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/SettingsViewModel.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/SettingsViewModel.kt
@@ -7,7 +7,7 @@ import androidx.lifecycle.AndroidViewModel
import androidx.preference.PreferenceManager
import com.v2ray.ang.AppConfig
import com.v2ray.ang.handler.MmkvManager
-import com.v2ray.ang.util.Utils
+import com.v2ray.ang.handler.SettingsManager
class SettingsViewModel(application: Application) : AndroidViewModel(application),
SharedPreferences.OnSharedPreferenceChangeListener {
@@ -83,7 +83,7 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
// }
}
if (key == AppConfig.PREF_UI_MODE_NIGHT) {
- Utils.setNightMode()
+ SettingsManager.setNightMode()
}
}
}
From 172d9fd0937f0bf4481cb8c8974b8ef7b2a39681 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Wed, 19 Mar 2025 10:43:00 +0800
Subject: [PATCH 059/238] Migrate SpeedtestUtil to SpeedtestManager
---
.../SpeedtestUtil.kt => handler/SpeedtestManager.kt} | 6 +++---
.../main/java/com/v2ray/ang/service/V2RayTestService.kt | 4 ++--
.../app/src/main/java/com/v2ray/ang/ui/AboutActivity.kt | 4 ++--
.../app/src/main/java/com/v2ray/ang/util/PluginUtil.kt | 3 ++-
.../main/java/com/v2ray/ang/viewmodel/MainViewModel.kt | 8 ++++----
5 files changed, 13 insertions(+), 12 deletions(-)
rename V2rayNG/app/src/main/java/com/v2ray/ang/{util/SpeedtestUtil.kt => handler/SpeedtestManager.kt} (97%)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/util/SpeedtestUtil.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SpeedtestManager.kt
similarity index 97%
rename from V2rayNG/app/src/main/java/com/v2ray/ang/util/SpeedtestUtil.kt
rename to V2rayNG/app/src/main/java/com/v2ray/ang/handler/SpeedtestManager.kt
index 166d9198..2b72ed16 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/util/SpeedtestUtil.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SpeedtestManager.kt
@@ -1,4 +1,4 @@
-package com.v2ray.ang.util
+package com.v2ray.ang.handler
import android.content.Context
import android.os.SystemClock
@@ -7,7 +7,7 @@ import android.util.Log
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
import com.v2ray.ang.extension.responseLength
-import com.v2ray.ang.handler.SettingsManager
+import com.v2ray.ang.util.HttpUtil
import kotlinx.coroutines.isActive
import libv2ray.Libv2ray
import java.io.IOException
@@ -16,7 +16,7 @@ import java.net.Socket
import java.net.UnknownHostException
import kotlin.coroutines.coroutineContext
-object SpeedtestUtil {
+object SpeedtestManager {
private val tcpTestingSockets = ArrayList()
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayTestService.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayTestService.kt
index 372cf714..4e546d58 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayTestService.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayTestService.kt
@@ -12,7 +12,7 @@ import com.v2ray.ang.handler.MmkvManager
import com.v2ray.ang.handler.V2rayConfigManager
import com.v2ray.ang.util.MessageUtil
import com.v2ray.ang.util.PluginUtil
-import com.v2ray.ang.util.SpeedtestUtil
+import com.v2ray.ang.handler.SpeedtestManager
import com.v2ray.ang.util.Utils
import go.Seq
import kotlinx.coroutines.CoroutineScope
@@ -65,7 +65,7 @@ class V2RayTestService : Service() {
if (!config.status) {
return retFailure
}
- return SpeedtestUtil.realPing(config.content)
+ return SpeedtestManager.realPing(config.content)
}
}
}
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/AboutActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/AboutActivity.kt
index 69838ac2..5dba1eb1 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/AboutActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/AboutActivity.kt
@@ -14,7 +14,7 @@ import com.v2ray.ang.AppConfig
import com.v2ray.ang.BuildConfig
import com.v2ray.ang.R
import com.v2ray.ang.databinding.ActivityAboutBinding
-import com.v2ray.ang.util.SpeedtestUtil
+import com.v2ray.ang.handler.SpeedtestManager
import com.v2ray.ang.util.Utils
import com.v2ray.ang.util.ZipUtil
import java.io.File
@@ -121,7 +121,7 @@ class AboutActivity : BaseActivity() {
Utils.openUri(this, AppConfig.v2rayNGPrivacyPolicy)
}
- "v${BuildConfig.VERSION_NAME} (${SpeedtestUtil.getLibVersion()})".also {
+ "v${BuildConfig.VERSION_NAME} (${SpeedtestManager.getLibVersion()})".also {
binding.tvVersion.text = it
}
}
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/util/PluginUtil.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/util/PluginUtil.kt
index a7566076..0f376f6f 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/util/PluginUtil.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/util/PluginUtil.kt
@@ -7,6 +7,7 @@ import com.v2ray.ang.AppConfig.ANG_PACKAGE
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.dto.ProfileItem
import com.v2ray.ang.fmt.Hysteria2Fmt
+import com.v2ray.ang.handler.SpeedtestManager
import com.v2ray.ang.service.ProcessService
import java.io.File
@@ -49,7 +50,7 @@ object PluginUtil {
val proc = ProcessService()
proc.runProcess(context, cmd)
Thread.sleep(1000L)
- val delay = SpeedtestUtil.testConnection(context, socksPort)
+ val delay = SpeedtestManager.testConnection(context, socksPort)
proc.stopProcess()
return delay.first
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/MainViewModel.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/MainViewModel.kt
index 74752a43..a97d980b 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/MainViewModel.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/MainViewModel.kt
@@ -24,7 +24,7 @@ import com.v2ray.ang.handler.AngConfigManager
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.handler.SpeedtestManager
import com.v2ray.ang.util.Utils
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -60,7 +60,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
override fun onCleared() {
getApplication().unregisterReceiver(mMsgReceiver)
tcpingTestScope.coroutineContext[Job]?.cancelChildren()
- SpeedtestUtil.closeAllTcpSockets()
+ SpeedtestManager.closeAllTcpSockets()
Log.i(ANG_PACKAGE, "Main ViewModel is cleared")
super.onCleared()
}
@@ -174,7 +174,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
fun testAllTcping() {
tcpingTestScope.coroutineContext[Job]?.cancelChildren()
- SpeedtestUtil.closeAllTcpSockets()
+ SpeedtestManager.closeAllTcpSockets()
MmkvManager.clearAllTestDelayResults(serversCache.map { it.guid }.toList())
//updateListAction.value = -1 // update all
@@ -185,7 +185,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
val serverPort = outbound.serverPort
if (serverAddress != null && serverPort != null) {
tcpingTestScope.launch {
- val testResult = SpeedtestUtil.tcping(serverAddress, serverPort.toInt())
+ val testResult = SpeedtestManager.tcping(serverAddress, serverPort.toInt())
launch(Dispatchers.Main) {
MmkvManager.encodeServerTestDelayMillis(item.guid, testResult)
updateListAction.value = getPosition(item.guid)
From faa4385087523891a6cda52f89d08cc01de699b4 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Wed, 19 Mar 2025 11:01:07 +0800
Subject: [PATCH 060/238] Using AI to improve function documentation
---
.../java/com/v2ray/ang/util/AppManagerUtil.kt | 6 +
.../main/java/com/v2ray/ang/util/HttpUtil.kt | 25 ++-
.../main/java/com/v2ray/ang/util/JsonUtil.kt | 25 +++
.../java/com/v2ray/ang/util/MessageUtil.kt | 30 ++-
.../com/v2ray/ang/util/MyContextWrapper.kt | 8 +-
.../java/com/v2ray/ang/util/PluginUtil.kt | 41 +++-
.../java/com/v2ray/ang/util/QRCodeDecoder.kt | 28 +--
.../src/main/java/com/v2ray/ang/util/Utils.kt | 192 ++++++++++++++++--
.../main/java/com/v2ray/ang/util/ZipUtil.kt | 25 ++-
9 files changed, 337 insertions(+), 43 deletions(-)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/util/AppManagerUtil.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/util/AppManagerUtil.kt
index e813384d..956000de 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/util/AppManagerUtil.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/util/AppManagerUtil.kt
@@ -8,6 +8,12 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
object AppManagerUtil {
+ /**
+ * Load the list of network applications.
+ *
+ * @param context The context to use.
+ * @return A list of AppInfo objects representing the network applications.
+ */
suspend fun loadNetworkAppList(context: Context): ArrayList =
withContext(Dispatchers.IO) {
val packageManager = context.packageManager
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt
index d3d41458..dc0ab7da 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt
@@ -10,11 +10,24 @@ import java.util.*
object HttpUtil {
+ /**
+ * Converts a URL string to its ASCII representation.
+ *
+ * @param str The URL string to convert.
+ * @return The ASCII representation of the URL.
+ */
fun idnToASCII(str: String): String {
val url = URL(str)
return URL(url.protocol, IDN.toASCII(url.host, IDN.ALLOW_UNASSIGNED), url.port, url.file).toExternalForm()
}
+ /**
+ * Retrieves the content of a URL as a string.
+ *
+ * @param url The URL to fetch content from.
+ * @param timeout The timeout value in milliseconds.
+ * @return The content of the URL as a string.
+ */
fun getUrlContent(url: String, timeout: Int): String {
var result: String = ""
val conn = createProxyConnection(url, 0, timeout, timeout) ?: return result
@@ -27,6 +40,15 @@ object HttpUtil {
return result
}
+ /**
+ * Retrieves the content of a URL as a string with a custom User-Agent header.
+ *
+ * @param url The URL to fetch content from.
+ * @param timeout The timeout value in milliseconds.
+ * @param httpPort The HTTP port to use.
+ * @return The content of the URL as a string.
+ * @throws IOException If an I/O error occurs.
+ */
@Throws(IOException::class)
fun getUrlContentWithUserAgent(url: String?, timeout: Int = 30000, httpPort: Int = 0): String {
var currentUrl = url
@@ -65,11 +87,10 @@ object HttpUtil {
* Creates an HttpURLConnection object connected through a proxy.
*
* @param urlStr The target URL address.
- * @param ip The IP address of the proxy server.
* @param port The port of the proxy server.
* @param connectTimeout The connection timeout in milliseconds (default is 30000 ms).
* @param readTimeout The read timeout in milliseconds (default is 30000 ms).
- * @param needStream
+ * @param needStream Whether the connection needs to support streaming.
* @return Returns a configured HttpURLConnection object, or null if it fails.
*/
fun createProxyConnection(
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/util/JsonUtil.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/util/JsonUtil.kt
index 28e7e956..04e0e167 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/util/JsonUtil.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/util/JsonUtil.kt
@@ -13,14 +13,33 @@ import java.lang.reflect.Type
object JsonUtil {
private var gson = Gson()
+ /**
+ * Converts an object to its JSON representation.
+ *
+ * @param src The object to convert.
+ * @return The JSON representation of the object.
+ */
fun toJson(src: Any?): String {
return gson.toJson(src)
}
+ /**
+ * Parses a JSON string into an object of the specified class.
+ *
+ * @param src The JSON string to parse.
+ * @param cls The class of the object to parse into.
+ * @return The parsed object.
+ */
fun fromJson(src: String, cls: Class): T {
return gson.fromJson(src, cls)
}
+ /**
+ * Converts an object to its pretty-printed JSON representation.
+ *
+ * @param src The object to convert.
+ * @return The pretty-printed JSON representation of the object, or null if the object is null.
+ */
fun toJsonPretty(src: Any?): String? {
if (src == null)
return null
@@ -39,6 +58,12 @@ object JsonUtil {
return gsonPre.toJson(src)
}
+ /**
+ * Parses a JSON string into a JsonObject.
+ *
+ * @param src The JSON string to parse.
+ * @return The parsed JsonObject, or null if parsing fails.
+ */
fun parseString(src: String?): JsonObject? {
if (src == null)
return null
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/util/MessageUtil.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/util/MessageUtil.kt
index 0f521f57..079e9138 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/util/MessageUtil.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/util/MessageUtil.kt
@@ -7,17 +7,37 @@ import com.v2ray.ang.AppConfig
import com.v2ray.ang.service.V2RayTestService
import java.io.Serializable
-
object MessageUtil {
+ /**
+ * Sends a message to the service.
+ *
+ * @param ctx The context.
+ * @param what The message identifier.
+ * @param content The message content.
+ */
fun sendMsg2Service(ctx: Context, what: Int, content: Serializable) {
sendMsg(ctx, AppConfig.BROADCAST_ACTION_SERVICE, what, content)
}
+ /**
+ * Sends a message to the UI.
+ *
+ * @param ctx The context.
+ * @param what The message identifier.
+ * @param content The message content.
+ */
fun sendMsg2UI(ctx: Context, what: Int, content: Serializable) {
sendMsg(ctx, AppConfig.BROADCAST_ACTION_ACTIVITY, what, content)
}
+ /**
+ * Sends a message to the test service.
+ *
+ * @param ctx The context.
+ * @param what The message identifier.
+ * @param content The message content.
+ */
fun sendMsg2TestService(ctx: Context, what: Int, content: Serializable) {
try {
val intent = Intent()
@@ -30,6 +50,14 @@ object MessageUtil {
}
}
+ /**
+ * Sends a message with the specified action.
+ *
+ * @param ctx The context.
+ * @param action The action string.
+ * @param what The message identifier.
+ * @param content The message content.
+ */
private fun sendMsg(ctx: Context, action: String, what: Int, content: Serializable) {
try {
val intent = Intent()
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/util/MyContextWrapper.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/util/MyContextWrapper.kt
index 8c0a4c63..a769368f 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/util/MyContextWrapper.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/util/MyContextWrapper.kt
@@ -11,12 +11,18 @@ import java.util.Locale
open class MyContextWrapper(base: Context?) : ContextWrapper(base) {
companion object {
+ /**
+ * Wraps the context with a new locale.
+ *
+ * @param context The original context.
+ * @param newLocale The new locale to set.
+ * @return A ContextWrapper with the new locale.
+ */
@RequiresApi(Build.VERSION_CODES.N)
fun wrap(context: Context, newLocale: Locale?): ContextWrapper {
var mContext = context
val res: Resources = mContext.resources
val configuration: Configuration = res.configuration
- //注意 Android 7.0 前后的不同处理方法
mContext = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
configuration.setLocale(newLocale)
val localeList = LocaleList(newLocale)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/util/PluginUtil.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/util/PluginUtil.kt
index 0f376f6f..a4d62a7b 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/util/PluginUtil.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/util/PluginUtil.kt
@@ -12,17 +12,19 @@ import com.v2ray.ang.service.ProcessService
import java.io.File
object PluginUtil {
- //private const val HYSTERIA2 = "hysteria2-plugin"
private const val HYSTERIA2 = "libhysteria2.so"
private const val TAG = ANG_PACKAGE
private val procService: ProcessService by lazy {
ProcessService()
}
-// fun initPlugin(name: String): PluginManager.InitResult {
-// return PluginManager.init(name)!!
-// }
-
+ /**
+ * Run the plugin based on the provided configuration.
+ *
+ * @param context The context to use.
+ * @param config The profile configuration.
+ * @param domainPort The domain and port information.
+ */
fun runPlugin(context: Context, config: ProfileItem?, domainPort: String?) {
Log.d(TAG, "runPlugin")
@@ -34,10 +36,20 @@ object PluginUtil {
}
}
+ /**
+ * Stop the running plugin.
+ */
fun stopPlugin() {
stopHy2()
}
+ /**
+ * Perform a real ping using Hysteria2.
+ *
+ * @param context The context to use.
+ * @param config The profile configuration.
+ * @return The ping delay in milliseconds, or -1 if it fails.
+ */
fun realPingHy2(context: Context, config: ProfileItem?): Long {
Log.d(TAG, "realPingHy2")
val retFailure = -1L
@@ -58,6 +70,14 @@ object PluginUtil {
return retFailure
}
+ /**
+ * Generate the configuration file for Hysteria2.
+ *
+ * @param context The context to use.
+ * @param config The profile configuration.
+ * @param domainPort The domain and port information.
+ * @return The generated configuration file.
+ */
private fun genConfigHy2(context: Context, config: ProfileItem, domainPort: String?): File? {
Log.d(TAG, "runPlugin $HYSTERIA2")
@@ -75,10 +95,16 @@ object PluginUtil {
return configFile
}
+ /**
+ * Generate the command to run Hysteria2.
+ *
+ * @param context The context to use.
+ * @param configFile The configuration file.
+ * @return The command to run Hysteria2.
+ */
private fun genCmdHy2(context: Context, configFile: File): MutableList {
return mutableListOf(
File(context.applicationInfo.nativeLibraryDir, HYSTERIA2).absolutePath,
- //initPlugin(HYSTERIA2).path,
"--disable-update-check",
"--config",
configFile.absolutePath,
@@ -88,6 +114,9 @@ object PluginUtil {
)
}
+ /**
+ * Stop the Hysteria2 process.
+ */
private fun stopHy2() {
try {
Log.d(TAG, "$HYSTERIA2 destroy")
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/util/QRCodeDecoder.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/util/QRCodeDecoder.kt
index fb6b2f68..446739b6 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/util/QRCodeDecoder.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/util/QRCodeDecoder.kt
@@ -14,13 +14,17 @@ import com.google.zxing.qrcode.QRCodeWriter
import java.util.EnumMap
/**
- * 描述:解析二维码图片
+ * QR code decoder utility.
*/
object QRCodeDecoder {
val HINTS: MutableMap = EnumMap(DecodeHintType::class.java)
/**
- * create qrcode using zxing
+ * Creates a QR code bitmap from the given text.
+ *
+ * @param text The text to encode in the QR code.
+ * @param size The size of the QR code bitmap.
+ * @return The generated QR code bitmap, or null if an error occurs.
*/
fun createQRCode(text: String, size: Int = 800): Bitmap? {
return runCatching {
@@ -35,22 +39,21 @@ object QRCodeDecoder {
}.getOrNull()
}
-
/**
- * 同步解析本地图片二维码。该方法是耗时操作,请在子线程中调用。
+ * Decodes a QR code from a local image file. This method is time-consuming and should be called in a background thread.
*
- * @param picturePath 要解析的二维码图片本地路径
- * @return 返回二维码图片里的内容 或 null
+ * @param picturePath The local path of the image file to decode.
+ * @return The content of the QR code, or null if decoding fails.
*/
fun syncDecodeQRCode(picturePath: String): String? {
return syncDecodeQRCode(getDecodeAbleBitmap(picturePath))
}
/**
- * 同步解析bitmap二维码。该方法是耗时操作,请在子线程中调用。
+ * Decodes a QR code from a bitmap. This method is time-consuming and should be called in a background thread.
*
- * @param bitmap 要解析的二维码图片
- * @return 返回二维码图片里的内容 或 null
+ * @param bitmap The bitmap to decode.
+ * @return The content of the QR code, or null if decoding fails.
*/
fun syncDecodeQRCode(bitmap: Bitmap?): String? {
return bitmap?.let {
@@ -70,12 +73,11 @@ object QRCodeDecoder {
}
}
-
/**
- * 将本地图片文件转换成可解码二维码的 Bitmap。为了避免图片太大,这里对图片进行了压缩。感谢 https://github.com/devilsen 提的 PR
+ * Converts a local image file to a bitmap that can be decoded as a QR code. The image is compressed to avoid being too large.
*
- * @param picturePath 本地图片文件路径
- * @return
+ * @param picturePath The local path of the image file.
+ * @return The decoded bitmap, or null if an error occurs.
*/
private fun getDecodeAbleBitmap(picturePath: String): Bitmap? {
return try {
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/util/Utils.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/util/Utils.kt
index 54bdd16f..ba3f64ff 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/util/Utils.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/util/Utils.kt
@@ -30,17 +30,21 @@ import java.util.UUID
object Utils {
/**
- * convert string to editalbe for kotlin
+ * Convert string to editable for Kotlin.
*
- * @param text
- * @return
+ * @param text The string to convert.
+ * @return An Editable instance containing the text.
*/
fun getEditable(text: String?): Editable {
return Editable.Factory.getInstance().newEditable(text.orEmpty())
}
/**
- * find value in array position
+ * Find the position of a value in an array.
+ *
+ * @param array The array to search.
+ * @param value The value to find.
+ * @return The index of the value in the array, or -1 if not found.
*/
fun arrayFind(array: Array, value: String): Int {
for (i in array.indices) {
@@ -52,19 +56,31 @@ object Utils {
}
/**
- * parseInt
+ * Parse a string to an integer.
+ *
+ * @param str The string to parse.
+ * @return The parsed integer, or 0 if parsing fails.
*/
fun parseInt(str: String): Int {
return parseInt(str, 0)
}
+ /**
+ * Parse a string to an integer with a default value.
+ *
+ * @param str The string to parse.
+ * @param default The default value if parsing fails.
+ * @return The parsed integer, or the default value if parsing fails.
+ */
fun parseInt(str: String?, default: Int): Int {
return str?.toIntOrNull() ?: default
}
-
/**
- * get text from clipboard
+ * Get text from the clipboard.
+ *
+ * @param context The context to use.
+ * @return The text from the clipboard, or an empty string if an error occurs.
*/
fun getClipboard(context: Context): String {
return try {
@@ -77,7 +93,10 @@ object Utils {
}
/**
- * set text to clipboard
+ * Set text to the clipboard.
+ *
+ * @param context The context to use.
+ * @param content The text to set to the clipboard.
*/
fun setClipboard(context: Context, content: String) {
try {
@@ -90,13 +109,21 @@ object Utils {
}
/**
- * base64 decode
+ * Decode a base64 encoded string.
+ *
+ * @param text The base64 encoded string.
+ * @return The decoded string, or an empty string if decoding fails.
*/
fun decode(text: String?): String {
return tryDecodeBase64(text) ?: text?.trimEnd('=')?.let { tryDecodeBase64(it) }.orEmpty()
}
-
+ /**
+ * Try to decode a base64 encoded string.
+ *
+ * @param text The base64 encoded string.
+ * @return The decoded string, or null if decoding fails.
+ */
fun tryDecodeBase64(text: String?): String? {
try {
return Base64.decode(text, Base64.NO_WRAP).toString(Charsets.UTF_8)
@@ -112,7 +139,10 @@ object Utils {
}
/**
- * base64 encode
+ * Encode a string to base64.
+ *
+ * @param text The string to encode.
+ * @return The base64 encoded string, or an empty string if encoding fails.
*/
fun encode(text: String): String {
return try {
@@ -123,9 +153,11 @@ object Utils {
}
}
-
/**
- * is ip address
+ * Check if a string is a valid IP address.
+ *
+ * @param value The string to check.
+ * @return True if the string is a valid IP address, false otherwise.
*/
fun isIpAddress(value: String?): Boolean {
try {
@@ -169,16 +201,34 @@ object Utils {
}
}
+ /**
+ * Check if a string is a pure IP address (IPv4 or IPv6).
+ *
+ * @param value The string to check.
+ * @return True if the string is a pure IP address, false otherwise.
+ */
fun isPureIpAddress(value: String): Boolean {
return isIpv4Address(value) || isIpv6Address(value)
}
+ /**
+ * Check if a string is a valid IPv4 address.
+ *
+ * @param value The string to check.
+ * @return True if the string is a valid IPv4 address, false otherwise.
+ */
private fun isIpv4Address(value: String): Boolean {
val regV4 =
Regex("^([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])$")
return regV4.matches(value)
}
+ /**
+ * Check if a string is a valid IPv6 address.
+ *
+ * @param value The string to check.
+ * @return True if the string is a valid IPv6 address, false otherwise.
+ */
private fun isIpv6Address(value: String): Boolean {
var addr = value
if (addr.indexOf("[") == 0 && addr.lastIndexOf("]") > 0) {
@@ -190,6 +240,12 @@ object Utils {
return regV6.matches(addr)
}
+ /**
+ * Check if a string is a CoreDNS address.
+ *
+ * @param s The string to check.
+ * @return True if the string is a CoreDNS address, false otherwise.
+ */
fun isCoreDNSAddress(s: String): Boolean {
return s.startsWith("https")
|| s.startsWith("tcp")
@@ -198,7 +254,10 @@ object Utils {
}
/**
- * is valid url
+ * Check if a string is a valid URL.
+ *
+ * @param value The string to check.
+ * @return True if the string is a valid URL, false otherwise.
*/
fun isValidUrl(value: String?): Boolean {
try {
@@ -218,14 +277,21 @@ object Utils {
return false
}
-
+ /**
+ * Open a URI in a browser.
+ *
+ * @param context The context to use.
+ * @param uriString The URI string to open.
+ */
fun openUri(context: Context, uriString: String) {
val uri = uriString.toUri()
context.startActivity(Intent(Intent.ACTION_VIEW, uri))
}
/**
- * uuid
+ * Generate a UUID.
+ *
+ * @return A UUID string without dashes.
*/
fun getUuid(): String {
return try {
@@ -236,6 +302,12 @@ object Utils {
}
}
+ /**
+ * Decode a URL-encoded string.
+ *
+ * @param url The URL-encoded string.
+ * @return The decoded string, or the original string if decoding fails.
+ */
fun urlDecode(url: String): String {
return try {
URLDecoder.decode(url, Charsets.UTF_8.toString())
@@ -245,6 +317,12 @@ object Utils {
}
}
+ /**
+ * Encode a string to URL-encoded format.
+ *
+ * @param url The string to encode.
+ * @return The URL-encoded string, or the original string if encoding fails.
+ */
fun urlEncode(url: String): String {
return try {
URLEncoder.encode(url, Charsets.UTF_8.toString()).replace("+", "%20")
@@ -254,9 +332,12 @@ object Utils {
}
}
-
/**
- * readTextFromAssets
+ * Read text from an asset file.
+ *
+ * @param context The context to use.
+ * @param fileName The name of the asset file.
+ * @return The content of the asset file as a string.
*/
fun readTextFromAssets(context: Context?, fileName: String): String {
if (context == null) {
@@ -268,6 +349,12 @@ object Utils {
return content
}
+ /**
+ * Get the path to the user asset directory.
+ *
+ * @param context The context to use.
+ * @return The path to the user asset directory.
+ */
fun userAssetPath(context: Context?): String {
if (context == null)
return ""
@@ -276,6 +363,12 @@ object Utils {
return extDir.absolutePath
}
+ /**
+ * Get the path to the backup directory.
+ *
+ * @param context The context to use.
+ * @return The path to the backup directory.
+ */
fun backupPath(context: Context?): String {
if (context == null)
return ""
@@ -284,15 +377,32 @@ object Utils {
return extDir.absolutePath
}
+ /**
+ * Get the device ID for XUDP base key.
+ *
+ * @return The device ID for XUDP base key.
+ */
fun getDeviceIdForXUDPBaseKey(): String {
val androidId = Settings.Secure.ANDROID_ID.toByteArray(Charsets.UTF_8)
return Base64.encodeToString(androidId.copyOf(32), Base64.NO_PADDING.or(Base64.URL_SAFE))
}
+ /**
+ * Get the dark mode status.
+ *
+ * @param context The context to use.
+ * @return True if dark mode is enabled, false otherwise.
+ */
fun getDarkModeStatus(context: Context): Boolean {
return context.resources.configuration.uiMode and UI_MODE_NIGHT_MASK != UI_MODE_NIGHT_NO
}
+ /**
+ * Get the IPv6 address in a formatted string.
+ *
+ * @param address The IPv6 address.
+ * @return The formatted IPv6 address, or the original address if not valid.
+ */
fun getIpv6Address(address: String?): String {
if (address == null) {
return ""
@@ -304,25 +414,55 @@ object Utils {
}
}
+ /**
+ * Get the system locale.
+ *
+ * @return The system locale.
+ */
fun getSysLocale(): Locale = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
LocaleList.getDefault()[0]
} else {
Locale.getDefault()
}
+ /**
+ * Fix illegal characters in a URL.
+ *
+ * @param str The URL string.
+ * @return The URL string with illegal characters replaced.
+ */
fun fixIllegalUrl(str: String): String {
return str
.replace(" ", "%20")
.replace("|", "%7C")
}
+ /**
+ * Remove white space from a string.
+ *
+ * @param str The string to process.
+ * @return The string without white space.
+ */
fun removeWhiteSpace(str: String?): String? {
return str?.replace(" ", "")
}
+ /**
+ * Check if the device is a TV.
+ *
+ * @param context The context to use.
+ * @return True if the device is a TV, false otherwise.
+ */
fun isTv(context: Context): Boolean =
context.packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
+ /**
+ * Find a free port from a list of ports.
+ *
+ * @param ports The list of ports to check.
+ * @return The first free port found.
+ * @throws IOException If no free port is found.
+ */
fun findFreePort(ports: List): Int {
for (port in ports) {
try {
@@ -336,6 +476,12 @@ object Utils {
throw IOException("no free port found")
}
+ /**
+ * Check if a string is a valid subscription URL.
+ *
+ * @param value The string to check.
+ * @return True if the string is a valid subscription URL, false otherwise.
+ */
fun isValidSubUrl(value: String?): Boolean {
try {
if (value.isNullOrEmpty()) return false
@@ -347,12 +493,22 @@ object Utils {
return false
}
+ /**
+ * Get the receiver flags based on the Android version.
+ *
+ * @return The receiver flags.
+ */
fun receiverFlags(): Int = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
ContextCompat.RECEIVER_EXPORTED
} else {
ContextCompat.RECEIVER_NOT_EXPORTED
}
+ /**
+ * Check if the package is Xray.
+ *
+ * @return True if the package is Xray, false otherwise.
+ */
fun isXray(): Boolean = (ANG_PACKAGE.startsWith("com.v2ray.ang"))
}
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/util/ZipUtil.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/util/ZipUtil.kt
index 5343f6d8..62677c5d 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/util/ZipUtil.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/util/ZipUtil.kt
@@ -13,6 +13,14 @@ import java.util.zip.ZipOutputStream
object ZipUtil {
private const val BUFFER_SIZE = 4096
+ /**
+ * Zip the contents of a folder.
+ *
+ * @param folderPath The path to the folder to zip.
+ * @param outputZipFilePath The path to the output zip file.
+ * @return True if the operation is successful, false otherwise.
+ * @throws IOException If an I/O error occurs.
+ */
@Throws(IOException::class)
fun zipFromFolder(folderPath: String, outputZipFilePath: String): Boolean {
val buffer = ByteArray(BUFFER_SIZE)
@@ -59,6 +67,14 @@ object ZipUtil {
return true
}
+ /**
+ * Unzip the contents of a zip file to a folder.
+ *
+ * @param zipFile The zip file to unzip.
+ * @param destDirectory The destination directory.
+ * @return True if the operation is successful, false otherwise.
+ * @throws IOException If an I/O error occurs.
+ */
@Throws(IOException::class)
fun unzipToFolder(zipFile: File, destDirectory: String): Boolean {
File(destDirectory).run {
@@ -72,10 +88,8 @@ object ZipUtil {
zip.getInputStream(entry).use { input ->
val filePath = destDirectory + File.separator + entry.name
if (!entry.isDirectory) {
- // if the entry is a file, extracts it
extractFile(input, filePath)
} else {
- // if the entry is a directory, make the directory
val dir = File(filePath)
dir.mkdir()
}
@@ -89,6 +103,13 @@ object ZipUtil {
return true
}
+ /**
+ * Extract a file from an input stream.
+ *
+ * @param inputStream The input stream to read from.
+ * @param destFilePath The destination file path.
+ * @throws IOException If an I/O error occurs.
+ */
@Throws(IOException::class)
private fun extractFile(inputStream: InputStream, destFilePath: String) {
val bos = BufferedOutputStream(FileOutputStream(destFilePath))
From 899e4c1b14d6c7f7553ce7fa8da8eceb8e58aa65 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Wed, 19 Mar 2025 11:41:12 +0800
Subject: [PATCH 061/238] Using AI to improve function documentation
---
.../com/v2ray/ang/receiver/BootReceiver.kt | 11 +++++++---
.../com/v2ray/ang/receiver/TaskerReceiver.kt | 9 +++++++-
.../com/v2ray/ang/receiver/WidgetProvider.kt | 22 ++++++++++++++++---
3 files changed, 35 insertions(+), 7 deletions(-)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/receiver/BootReceiver.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/receiver/BootReceiver.kt
index 1d96f750..ae15e76c 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/receiver/BootReceiver.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/receiver/BootReceiver.kt
@@ -7,12 +7,17 @@ import com.v2ray.ang.handler.MmkvManager
import com.v2ray.ang.service.V2RayServiceManager
class BootReceiver : BroadcastReceiver() {
+ /**
+ * This method is called when the BroadcastReceiver is receiving an Intent broadcast.
+ * It checks if the context is not null and the action is ACTION_BOOT_COMPLETED.
+ * If the conditions are met, it starts the V2Ray service.
+ *
+ * @param context The Context in which the receiver is running.
+ * @param intent The Intent being received.
+ */
override fun onReceive(context: Context?, intent: Intent?) {
- //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.startVService(context)
}
}
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/receiver/TaskerReceiver.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/receiver/TaskerReceiver.kt
index b0aa16f4..eece9c3e 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/receiver/TaskerReceiver.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/receiver/TaskerReceiver.kt
@@ -9,8 +9,15 @@ import com.v2ray.ang.service.V2RayServiceManager
class TaskerReceiver : BroadcastReceiver() {
+ /**
+ * This method is called when the BroadcastReceiver is receiving an Intent broadcast.
+ * It retrieves the bundle from the intent and checks the switch and guid values.
+ * Depending on the switch value, it starts or stops the V2Ray service.
+ *
+ * @param context The Context in which the receiver is running.
+ * @param intent The Intent being received.
+ */
override fun onReceive(context: Context, intent: Intent?) {
-
try {
val bundle = intent?.getBundleExtra(AppConfig.TASKER_EXTRA_BUNDLE)
val switch = bundle?.getBoolean(AppConfig.TASKER_EXTRA_BUNDLE_SWITCH, false)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/receiver/WidgetProvider.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/receiver/WidgetProvider.kt
index bfe05f60..99d9bda2 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/receiver/WidgetProvider.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/receiver/WidgetProvider.kt
@@ -14,14 +14,26 @@ import com.v2ray.ang.service.V2RayServiceManager
class WidgetProvider : AppWidgetProvider() {
/**
- * 每次窗口小部件被更新都调用一次该方法
+ * This method is called every time the widget is updated.
+ * It updates the widget background based on the V2Ray service running state.
+ *
+ * @param context The Context in which the receiver is running.
+ * @param appWidgetManager The AppWidgetManager instance.
+ * @param appWidgetIds The appWidgetIds for which an update is needed.
*/
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
super.onUpdate(context, appWidgetManager, appWidgetIds)
updateWidgetBackground(context, appWidgetManager, appWidgetIds, V2RayServiceManager.isRunning())
}
-
+ /**
+ * Updates the widget background based on whether the V2Ray service is running.
+ *
+ * @param context The Context in which the receiver is running.
+ * @param appWidgetManager The AppWidgetManager instance.
+ * @param appWidgetIds The appWidgetIds for which an update is needed.
+ * @param isRunning Boolean indicating if the V2Ray service is running.
+ */
private fun updateWidgetBackground(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray, isRunning: Boolean) {
val remoteViews = RemoteViews(context.packageName, R.layout.widget_switch)
val intent = Intent(context, WidgetProvider::class.java)
@@ -51,7 +63,11 @@ class WidgetProvider : AppWidgetProvider() {
}
/**
- * 接收窗口小部件发送的广播
+ * This method is called when the BroadcastReceiver is receiving an Intent broadcast.
+ * It handles widget click actions and updates the widget background based on the V2Ray service state.
+ *
+ * @param context The Context in which the receiver is running.
+ * @param intent The Intent being received.
*/
override fun onReceive(context: Context, intent: Intent) {
super.onReceive(context, intent)
From ef4145787b0a5edcf2c886516f69f901b2eb941f Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Wed, 19 Mar 2025 11:57:56 +0800
Subject: [PATCH 062/238] Using AI to improve function documentation
---
.../com/v2ray/ang/handler/AngConfigManager.kt | 91 ++++++-
.../com/v2ray/ang/handler/MigrateManager.kt | 54 ++++-
.../java/com/v2ray/ang/handler/MmkvManager.kt | 224 +++++++++++++++++-
.../com/v2ray/ang/handler/SettingsManager.kt | 96 +++++++-
.../com/v2ray/ang/handler/SpeedtestManager.kt | 43 +++-
.../v2ray/ang/handler/V2rayConfigManager.kt | 20 ++
6 files changed, 508 insertions(+), 20 deletions(-)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/AngConfigManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/AngConfigManager.kt
index 865a0817..10e28b4c 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/AngConfigManager.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/AngConfigManager.kt
@@ -23,8 +23,15 @@ import com.v2ray.ang.util.Utils
import java.net.URI
object AngConfigManager {
+
/**
- * parse config form qrcode or...
+ * Parses the configuration from a QR code or string.
+ *
+ * @param str The configuration string.
+ * @param subid The subscription ID.
+ * @param subItem The subscription item.
+ * @param removedSelectedServer The removed selected server.
+ * @return The result code.
*/
private fun parseConfig(
str: String?,
@@ -80,7 +87,10 @@ object AngConfigManager {
}
/**
- * share config
+ * Shares the configuration.
+ *
+ * @param guid The GUID of the configuration.
+ * @return The configuration string.
*/
private fun shareConfig(guid: String): String {
try {
@@ -104,7 +114,11 @@ object AngConfigManager {
}
/**
- * share2Clipboard
+ * Shares the configuration to the clipboard.
+ *
+ * @param context The context.
+ * @param guid The GUID of the configuration.
+ * @return The result code.
*/
fun share2Clipboard(context: Context, guid: String): Int {
try {
@@ -123,7 +137,11 @@ object AngConfigManager {
}
/**
- * share2Clipboard
+ * Shares non-custom configurations to the clipboard.
+ *
+ * @param context The context.
+ * @param serverList The list of server GUIDs.
+ * @return The number of configurations shared.
*/
fun shareNonCustomConfigsToClipboard(context: Context, serverList: List): Int {
try {
@@ -147,7 +165,10 @@ object AngConfigManager {
}
/**
- * share2QRCode
+ * Shares the configuration as a QR code.
+ *
+ * @param guid The GUID of the configuration.
+ * @return The QR code bitmap.
*/
fun share2QRCode(guid: String): Bitmap? {
try {
@@ -164,7 +185,11 @@ object AngConfigManager {
}
/**
- * shareFullContent2Clipboard
+ * Shares the full content of the configuration to the clipboard.
+ *
+ * @param context The context.
+ * @param guid The GUID of the configuration.
+ * @return The result code.
*/
fun shareFullContent2Clipboard(context: Context, guid: String?): Int {
try {
@@ -189,6 +214,14 @@ object AngConfigManager {
return 0
}
+ /**
+ * Imports a batch of configurations.
+ *
+ * @param server The server string.
+ * @param subid The subscription ID.
+ * @param append Whether to append the configurations.
+ * @return A pair containing the number of configurations and subscriptions imported.
+ */
fun importBatchConfig(server: String?, subid: String, append: Boolean): Pair {
var count = parseBatchConfig(Utils.decode(server), subid, append)
if (count <= 0) {
@@ -209,6 +242,12 @@ object AngConfigManager {
return count to countSub
}
+ /**
+ * Parses a batch of subscriptions.
+ *
+ * @param servers The servers string.
+ * @return The number of subscriptions parsed.
+ */
fun parseBatchSubscription(servers: String?): Int {
try {
if (servers == null) {
@@ -230,6 +269,14 @@ object AngConfigManager {
return 0
}
+ /**
+ * Parses a batch of configurations.
+ *
+ * @param servers The servers string.
+ * @param subid The subscription ID.
+ * @param append Whether to append the configurations.
+ * @return The number of configurations parsed.
+ */
fun parseBatchConfig(servers: String?, subid: String, append: Boolean): Int {
try {
if (servers == null) {
@@ -270,6 +317,13 @@ object AngConfigManager {
return 0
}
+ /**
+ * Parses a custom configuration server.
+ *
+ * @param server The server string.
+ * @param subid The subscription ID.
+ * @return The number of configurations parsed.
+ */
fun parseCustomConfigServer(server: String?, subid: String): Int {
if (server == null) {
return 0
@@ -323,6 +377,11 @@ object AngConfigManager {
}
}
+ /**
+ * Updates the configuration via all subscriptions.
+ *
+ * @return The number of configurations updated.
+ */
fun updateConfigViaSubAll(): Int {
var count = 0
try {
@@ -336,6 +395,12 @@ object AngConfigManager {
return count
}
+ /**
+ * Updates the configuration via a subscription.
+ *
+ * @param it The subscription item.
+ * @return The number of configurations updated.
+ */
fun updateConfigViaSub(it: Pair): Int {
try {
if (TextUtils.isEmpty(it.first)
@@ -379,6 +444,14 @@ object AngConfigManager {
}
}
+ /**
+ * Parses the configuration via a subscription.
+ *
+ * @param server The server string.
+ * @param subid The subscription ID.
+ * @param append Whether to append the configurations.
+ * @return The number of configurations parsed.
+ */
private fun parseConfigViaSub(server: String?, subid: String, append: Boolean): Int {
var count = parseBatchConfig(Utils.decode(server), subid, append)
if (count <= 0) {
@@ -390,6 +463,12 @@ object AngConfigManager {
return count
}
+ /**
+ * Imports a URL as a subscription.
+ *
+ * @param url The URL.
+ * @return The number of subscriptions imported.
+ */
private fun importUrlAsSubscription(url: String): Int {
val subscriptions = MmkvManager.decodeSubscriptions()
subscriptions.forEach {
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/MigrateManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/MigrateManager.kt
index a6b63412..952d0a0b 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/MigrateManager.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/MigrateManager.kt
@@ -16,6 +16,11 @@ object MigrateManager {
private const val ID_SERVER_CONFIG = "SERVER_CONFIG"
private val serverStorage by lazy { MMKV.mmkvWithID(ID_SERVER_CONFIG, MMKV.MULTI_PROCESS_MODE) }
+ /**
+ * Migrates server configurations to profile items.
+ *
+ * @return True if migration was successful, false otherwise.
+ */
fun migrateServerConfig2Profile(): Boolean {
if (serverStorage.count().toInt() == 0) {
return false
@@ -44,6 +49,12 @@ object MigrateManager {
return true
}
+ /**
+ * Migrates a server configuration to a profile item.
+ *
+ * @param configOld The old server configuration.
+ * @return The profile item.
+ */
private fun migrateServerConfig2ProfileSub(configOld: ServerConfig): ProfileItem? {
return when (configOld.getProxyOutbound()?.protocol) {
EConfigType.VMESS.name.lowercase() -> migrate2ProfileCommon(configOld)
@@ -62,6 +73,12 @@ object MigrateManager {
}
}
+ /**
+ * Migrates a common server configuration to a profile item.
+ *
+ * @param configOld The old server configuration.
+ * @return The profile item.
+ */
private fun migrate2ProfileCommon(configOld: ServerConfig): ProfileItem? {
val config = ProfileItem.create(configOld.configType)
@@ -101,6 +118,12 @@ object MigrateManager {
return config
}
+ /**
+ * Migrates a SOCKS server configuration to a profile item.
+ *
+ * @param configOld The old server configuration.
+ * @return The profile item.
+ */
private fun migrate2ProfileSocks(configOld: ServerConfig): ProfileItem? {
val config = ProfileItem.create(EConfigType.SOCKS)
@@ -114,6 +137,12 @@ object MigrateManager {
return config
}
+ /**
+ * Migrates an HTTP server configuration to a profile item.
+ *
+ * @param configOld The old server configuration.
+ * @return The profile item.
+ */
private fun migrate2ProfileHttp(configOld: ServerConfig): ProfileItem? {
val config = ProfileItem.create(EConfigType.HTTP)
@@ -127,6 +156,12 @@ object MigrateManager {
return config
}
+ /**
+ * Migrates a WireGuard server configuration to a profile item.
+ *
+ * @param configOld The old server configuration.
+ * @return The profile item.
+ */
private fun migrate2ProfileWireguard(configOld: ServerConfig): ProfileItem? {
val config = ProfileItem.create(EConfigType.WIREGUARD)
@@ -145,6 +180,12 @@ object MigrateManager {
return config
}
+ /**
+ * Migrates a Hysteria2 server configuration to a profile item.
+ *
+ * @param configOld The old server configuration.
+ * @return The profile item.
+ */
private fun migrate2ProfileHysteria2(configOld: ServerConfig): ProfileItem? {
val config = ProfileItem.create(EConfigType.HYSTERIA2)
@@ -166,6 +207,12 @@ object MigrateManager {
return config
}
+ /**
+ * Migrates a custom server configuration to a profile item.
+ *
+ * @param configOld The old server configuration.
+ * @return The profile item.
+ */
private fun migrate2ProfileCustom(configOld: ServerConfig): ProfileItem? {
val config = ProfileItem.create(EConfigType.CUSTOM)
@@ -177,7 +224,12 @@ object MigrateManager {
return config
}
-
+ /**
+ * Decodes the old server configuration.
+ *
+ * @param guid The server GUID.
+ * @return The old server configuration.
+ */
private fun decodeServerConfigOld(guid: String): ServerConfig? {
if (guid.isBlank()) {
return null
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/MmkvManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/MmkvManager.kt
index 773699d0..3c489ddc 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/MmkvManager.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/MmkvManager.kt
@@ -1,6 +1,5 @@
package com.v2ray.ang.handler
-
import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig.PREF_IS_BOOTED
import com.v2ray.ang.AppConfig.PREF_ROUTING_RULESET
@@ -41,18 +40,38 @@ object MmkvManager {
//region Server
+ /**
+ * Gets the selected server GUID.
+ *
+ * @return The selected server GUID.
+ */
fun getSelectServer(): String? {
return mainStorage.decodeString(KEY_SELECTED_SERVER)
}
+ /**
+ * Sets the selected server GUID.
+ *
+ * @param guid The server GUID.
+ */
fun setSelectServer(guid: String) {
mainStorage.encode(KEY_SELECTED_SERVER, guid)
}
+ /**
+ * Encodes the server list.
+ *
+ * @param serverList The list of server GUIDs.
+ */
fun encodeServerList(serverList: MutableList) {
mainStorage.encode(KEY_ANG_CONFIGS, JsonUtil.toJson(serverList))
}
+ /**
+ * Decodes the server list.
+ *
+ * @return The list of server GUIDs.
+ */
fun decodeServerList(): MutableList {
val json = mainStorage.decodeString(KEY_ANG_CONFIGS)
return if (json.isNullOrBlank()) {
@@ -62,7 +81,12 @@ object MmkvManager {
}
}
-
+ /**
+ * Decodes the server configuration.
+ *
+ * @param guid The server GUID.
+ * @return The server configuration.
+ */
fun decodeServerConfig(guid: String): ProfileItem? {
if (guid.isBlank()) {
return null
@@ -85,6 +109,13 @@ object MmkvManager {
// return JsonUtil.fromJson(json, ProfileLiteItem::class.java)
// }
+ /**
+ * Encodes the server configuration.
+ *
+ * @param guid The server GUID.
+ * @param config The server configuration.
+ * @return The server GUID.
+ */
fun encodeServerConfig(guid: String, config: ProfileItem): String {
val key = guid.ifBlank { Utils.getUuid() }
profileFullStorage.encode(key, JsonUtil.toJson(config))
@@ -107,6 +138,11 @@ object MmkvManager {
return key
}
+ /**
+ * Removes the server configuration.
+ *
+ * @param guid The server GUID.
+ */
fun removeServer(guid: String) {
if (guid.isBlank()) {
return
@@ -122,6 +158,11 @@ object MmkvManager {
serverAffStorage.remove(guid)
}
+ /**
+ * Removes the server configurations via subscription ID.
+ *
+ * @param subid The subscription ID.
+ */
fun removeServerViaSubid(subid: String) {
if (subid.isBlank()) {
return
@@ -135,6 +176,12 @@ object MmkvManager {
}
}
+ /**
+ * Decodes the server affiliation information.
+ *
+ * @param guid The server GUID.
+ * @return The server affiliation information.
+ */
fun decodeServerAffiliationInfo(guid: String): ServerAffiliationInfo? {
if (guid.isBlank()) {
return null
@@ -146,6 +193,12 @@ object MmkvManager {
return JsonUtil.fromJson(json, ServerAffiliationInfo::class.java)
}
+ /**
+ * Encodes the server test delay in milliseconds.
+ *
+ * @param guid The server GUID.
+ * @param testResult The test delay in milliseconds.
+ */
fun encodeServerTestDelayMillis(guid: String, testResult: Long) {
if (guid.isBlank()) {
return
@@ -155,6 +208,11 @@ object MmkvManager {
serverAffStorage.encode(guid, JsonUtil.toJson(aff))
}
+ /**
+ * Clears all test delay results.
+ *
+ * @param keys The list of server GUIDs.
+ */
fun clearAllTestDelayResults(keys: List?) {
keys?.forEach { key ->
decodeServerAffiliationInfo(key)?.let { aff ->
@@ -164,6 +222,11 @@ object MmkvManager {
}
}
+ /**
+ * Removes all server configurations.
+ *
+ * @return The number of server configurations removed.
+ */
fun removeAllServer(): Int {
val count = profileFullStorage.allKeys()?.count() ?: 0
mainStorage.clearAll()
@@ -173,6 +236,12 @@ object MmkvManager {
return count
}
+ /**
+ * Removes invalid server configurations.
+ *
+ * @param guid The server GUID.
+ * @return The number of server configurations removed.
+ */
fun removeInvalidServer(guid: String): Int {
var count = 0
if (guid.isNotEmpty()) {
@@ -195,10 +264,22 @@ object MmkvManager {
return count
}
+ /**
+ * Encodes the raw server configuration.
+ *
+ * @param guid The server GUID.
+ * @param config The raw server configuration.
+ */
fun encodeServerRaw(guid: String, config: String) {
serverRawStorage.encode(guid, config)
}
+ /**
+ * Decodes the raw server configuration.
+ *
+ * @param guid The server GUID.
+ * @return The raw server configuration.
+ */
fun decodeServerRaw(guid: String): String? {
return serverRawStorage.decodeString(guid)
}
@@ -207,6 +288,9 @@ object MmkvManager {
//region Subscriptions
+ /**
+ * Initializes the subscription list.
+ */
private fun initSubsList() {
val subsList = decodeSubsList()
if (subsList.isNotEmpty()) {
@@ -218,6 +302,11 @@ object MmkvManager {
encodeSubsList(subsList)
}
+ /**
+ * Decodes the subscriptions.
+ *
+ * @return The list of subscriptions.
+ */
fun decodeSubscriptions(): List> {
initSubsList()
@@ -231,6 +320,11 @@ object MmkvManager {
return subscriptions
}
+ /**
+ * Removes the subscription.
+ *
+ * @param subid The subscription ID.
+ */
fun removeSubscription(subid: String) {
subStorage.remove(subid)
val subsList = decodeSubsList()
@@ -240,6 +334,12 @@ object MmkvManager {
removeServerViaSubid(subid)
}
+ /**
+ * Encodes the subscription.
+ *
+ * @param guid The subscription GUID.
+ * @param subItem The subscription item.
+ */
fun encodeSubscription(guid: String, subItem: SubscriptionItem) {
val key = guid.ifBlank { Utils.getUuid() }
subStorage.encode(key, JsonUtil.toJson(subItem))
@@ -251,15 +351,31 @@ object MmkvManager {
}
}
+ /**
+ * Decodes the subscription.
+ *
+ * @param subscriptionId The subscription ID.
+ * @return The subscription item.
+ */
fun decodeSubscription(subscriptionId: String): SubscriptionItem? {
val json = subStorage.decodeString(subscriptionId) ?: return null
return JsonUtil.fromJson(json, SubscriptionItem::class.java)
}
+ /**
+ * Encodes the subscription list.
+ *
+ * @param subsList The list of subscription IDs.
+ */
fun encodeSubsList(subsList: MutableList) {
mainStorage.encode(KEY_SUB_IDS, JsonUtil.toJson(subsList))
}
+ /**
+ * Decodes the subscription list.
+ *
+ * @return The list of subscription IDs.
+ */
fun decodeSubsList(): MutableList {
val json = mainStorage.decodeString(KEY_SUB_IDS)
return if (json.isNullOrBlank()) {
@@ -273,6 +389,11 @@ object MmkvManager {
//region Asset
+ /**
+ * Decodes the asset URLs.
+ *
+ * @return The list of asset URLs.
+ */
fun decodeAssetUrls(): List> {
val assetUrlItems = mutableListOf>()
assetStorage.allKeys()?.forEach { key ->
@@ -284,15 +405,32 @@ object MmkvManager {
return assetUrlItems.sortedBy { (_, value) -> value.addedTime }
}
+ /**
+ * Removes the asset URL.
+ *
+ * @param assetid The asset ID.
+ */
fun removeAssetUrl(assetid: String) {
assetStorage.remove(assetid)
}
+ /**
+ * Encodes the asset.
+ *
+ * @param assetid The asset ID.
+ * @param assetItem The asset item.
+ */
fun encodeAsset(assetid: String, assetItem: AssetUrlItem) {
val key = assetid.ifBlank { Utils.getUuid() }
assetStorage.encode(key, JsonUtil.toJson(assetItem))
}
+ /**
+ * Decodes the asset.
+ *
+ * @param assetid The asset ID.
+ * @return The asset item.
+ */
fun decodeAsset(assetid: String): AssetUrlItem? {
val json = assetStorage.decodeString(assetid) ?: return null
return JsonUtil.fromJson(json, AssetUrlItem::class.java)
@@ -302,12 +440,22 @@ object MmkvManager {
//region Routing
+ /**
+ * Decodes the routing rulesets.
+ *
+ * @return The list of routing rulesets.
+ */
fun decodeRoutingRulesets(): MutableList? {
val ruleset = settingsStorage.decodeString(PREF_ROUTING_RULESET)
if (ruleset.isNullOrEmpty()) return null
return JsonUtil.fromJson(ruleset, Array::class.java).toMutableList()
}
+ /**
+ * Encodes the routing rulesets.
+ *
+ * @param rulesetList The list of routing rulesets.
+ */
fun encodeRoutingRulesets(rulesetList: MutableList?) {
if (rulesetList.isNullOrEmpty())
encodeSettings(PREF_ROUTING_RULESET, "")
@@ -316,39 +464,99 @@ object MmkvManager {
}
//endregion
+
+ /**
+ * Encodes the settings.
+ *
+ * @param key The settings key.
+ * @param value The settings value.
+ * @return Whether the encoding was successful.
+ */
fun encodeSettings(key: String, value: String?): Boolean {
return settingsStorage.encode(key, value)
}
+ /**
+ * Encodes the settings.
+ *
+ * @param key The settings key.
+ * @param value The settings value.
+ * @return Whether the encoding was successful.
+ */
fun encodeSettings(key: String, value: Int): Boolean {
return settingsStorage.encode(key, value)
}
+ /**
+ * Encodes the settings.
+ *
+ * @param key The settings key.
+ * @param value The settings value.
+ * @return Whether the encoding was successful.
+ */
fun encodeSettings(key: String, value: Boolean): Boolean {
return settingsStorage.encode(key, value)
}
+ /**
+ * Encodes the settings.
+ *
+ * @param key The settings key.
+ * @param value The settings value.
+ * @return Whether the encoding was successful.
+ */
fun encodeSettings(key: String, value: MutableSet): Boolean {
return settingsStorage.encode(key, value)
}
-
+ /**
+ * Decodes the settings string.
+ *
+ * @param key The settings key.
+ * @return The settings value.
+ */
fun decodeSettingsString(key: String): String? {
return settingsStorage.decodeString(key)
}
+ /**
+ * Decodes the settings string.
+ *
+ * @param key The settings key.
+ * @param defaultValue The default value.
+ * @return The settings value.
+ */
fun decodeSettingsString(key: String, defaultValue: String?): String? {
return settingsStorage.decodeString(key, defaultValue)
}
+ /**
+ * Decodes the settings boolean.
+ *
+ * @param key The settings key.
+ * @return The settings value.
+ */
fun decodeSettingsBool(key: String): Boolean {
return settingsStorage.decodeBool(key, false)
}
+ /**
+ * Decodes the settings boolean.
+ *
+ * @param key The settings key.
+ * @param defaultValue The default value.
+ * @return The settings value.
+ */
fun decodeSettingsBool(key: String, defaultValue: Boolean): Boolean {
return settingsStorage.decodeBool(key, defaultValue)
}
+ /**
+ * Decodes the settings string set.
+ *
+ * @param key The settings key.
+ * @return The settings value.
+ */
fun decodeSettingsStringSet(key: String): MutableSet? {
return settingsStorage.decodeStringSet(key)
}
@@ -357,10 +565,20 @@ object MmkvManager {
//region Others
+ /**
+ * Encodes the start on boot setting.
+ *
+ * @param startOnBoot Whether to start on boot.
+ */
fun encodeStartOnBoot(startOnBoot: Boolean) {
MmkvManager.encodeSettings(PREF_IS_BOOTED, startOnBoot)
}
+ /**
+ * Decodes the start on boot setting.
+ *
+ * @return Whether to start on boot.
+ */
fun decodeStartOnBoot(): Boolean {
return decodeSettingsBool(PREF_IS_BOOTED, false)
}
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SettingsManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SettingsManager.kt
index 50c88dba..05f18f48 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SettingsManager.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SettingsManager.kt
@@ -27,6 +27,10 @@ import java.util.Locale
object SettingsManager {
+ /**
+ * Initialize routing rulesets.
+ * @param context The application context.
+ */
fun initRoutingRulesets(context: Context) {
val exist = MmkvManager.decodeRoutingRulesets()
if (exist.isNullOrEmpty()) {
@@ -35,6 +39,12 @@ object SettingsManager {
}
}
+ /**
+ * Get preset routing rulesets.
+ * @param context The application context.
+ * @param index The index of the routing type.
+ * @return A mutable list of RulesetItem.
+ */
private fun getPresetRoutingRulesets(context: Context, index: Int = 0): MutableList? {
val fileName = RoutingType.fromIndex(index).fileName
val assets = Utils.readTextFromAssets(context, fileName)
@@ -45,12 +55,21 @@ object SettingsManager {
return JsonUtil.fromJson(assets, Array::class.java).toMutableList()
}
-
+ /**
+ * Reset routing rulesets from presets.
+ * @param context The application context.
+ * @param index The index of the routing type.
+ */
fun resetRoutingRulesetsFromPresets(context: Context, index: Int) {
val rulesetList = getPresetRoutingRulesets(context, index) ?: return
resetRoutingRulesetsCommon(rulesetList)
}
+ /**
+ * Reset routing rulesets.
+ * @param content The content of the rulesets.
+ * @return True if successful, false otherwise.
+ */
fun resetRoutingRulesets(content: String?): Boolean {
if (content.isNullOrEmpty()) {
return false
@@ -70,6 +89,10 @@ object SettingsManager {
}
}
+ /**
+ * Common method to reset routing rulesets.
+ * @param rulesetList The list of rulesets.
+ */
private fun resetRoutingRulesetsCommon(rulesetList: MutableList) {
val rulesetNew: MutableList = mutableListOf()
MmkvManager.decodeRoutingRulesets()?.forEach { key ->
@@ -82,6 +105,11 @@ object SettingsManager {
MmkvManager.encodeRoutingRulesets(rulesetNew)
}
+ /**
+ * Get a routing ruleset by index.
+ * @param index The index of the ruleset.
+ * @return The RulesetItem.
+ */
fun getRoutingRuleset(index: Int): RulesetItem? {
if (index < 0) return null
@@ -91,6 +119,11 @@ object SettingsManager {
return rulesetList[index]
}
+ /**
+ * Save a routing ruleset.
+ * @param index The index of the ruleset.
+ * @param ruleset The RulesetItem to save.
+ */
fun saveRoutingRuleset(index: Int, ruleset: RulesetItem?) {
if (ruleset == null) return
@@ -107,6 +140,10 @@ object SettingsManager {
MmkvManager.encodeRoutingRulesets(rulesetList)
}
+ /**
+ * Remove a routing ruleset by index.
+ * @param index The index of the ruleset.
+ */
fun removeRoutingRuleset(index: Int) {
if (index < 0) return
@@ -117,6 +154,10 @@ object SettingsManager {
MmkvManager.encodeRoutingRulesets(rulesetList)
}
+ /**
+ * Check if routing rulesets bypass LAN.
+ * @return True if bypassing LAN, false otherwise.
+ */
fun routingRulesetsBypassLan(): Boolean {
val vpnBypassLan = MmkvManager.decodeSettingsString(AppConfig.PREF_VPN_BYPASS_LAN) ?: "0"
if (vpnBypassLan == "1") {
@@ -125,7 +166,6 @@ object SettingsManager {
return false
}
- //Follow config
val guid = MmkvManager.getSelectServer() ?: return false
val config = MmkvManager.decodeServerConfig(guid) ?: return false
if (config.configType == EConfigType.CUSTOM) {
@@ -144,6 +184,11 @@ object SettingsManager {
return exist == true
}
+ /**
+ * Swap routing rulesets.
+ * @param fromPosition The position to swap from.
+ * @param toPosition The position to swap to.
+ */
fun swapRoutingRuleset(fromPosition: Int, toPosition: Int) {
val rulesetList = MmkvManager.decodeRoutingRulesets()
if (rulesetList.isNullOrEmpty()) return
@@ -152,6 +197,11 @@ object SettingsManager {
MmkvManager.encodeRoutingRulesets(rulesetList)
}
+ /**
+ * Swap subscriptions.
+ * @param fromPosition The position to swap from.
+ * @param toPosition The position to swap to.
+ */
fun swapSubscriptions(fromPosition: Int, toPosition: Int) {
val subsList = MmkvManager.decodeSubsList()
if (subsList.isNullOrEmpty()) return
@@ -160,6 +210,11 @@ object SettingsManager {
MmkvManager.encodeSubsList(subsList)
}
+ /**
+ * Get server via remarks.
+ * @param remarks The remarks of the server.
+ * @return The ProfileItem.
+ */
fun getServerViaRemarks(remarks: String?): ProfileItem? {
if (remarks == null) {
return null
@@ -174,14 +229,27 @@ object SettingsManager {
return null
}
+ /**
+ * Get the SOCKS port.
+ * @return The SOCKS port.
+ */
fun getSocksPort(): Int {
return Utils.parseInt(MmkvManager.decodeSettingsString(AppConfig.PREF_SOCKS_PORT), AppConfig.PORT_SOCKS.toInt())
}
+ /**
+ * Get the HTTP port.
+ * @return The HTTP port.
+ */
fun getHttpPort(): Int {
return getSocksPort() + (if (Utils.isXray()) 0 else 1)
}
+ /**
+ * Initialize assets.
+ * @param context The application context.
+ * @param assets The AssetManager.
+ */
fun initAssets(context: Context, assets: AssetManager) {
val extFolder = Utils.userAssetPath(context)
@@ -205,11 +273,11 @@ object SettingsManager {
} catch (e: Exception) {
Log.e(ANG_PACKAGE, "asset copy failed", e)
}
-
}
/**
- * get domestic dns servers from preference
+ * Get domestic DNS servers from preference.
+ * @return A list of domestic DNS servers.
*/
fun getDomesticDnsServers(): List {
val domesticDns =
@@ -222,7 +290,8 @@ object SettingsManager {
}
/**
- * get remote dns servers from preference
+ * Get remote DNS servers from preference.
+ * @return A list of remote DNS servers.
*/
fun getRemoteDnsServers(): List {
val remoteDns =
@@ -235,15 +304,19 @@ object SettingsManager {
}
/**
- * get vpn dns servers from preference
+ * Get VPN DNS servers from preference.
+ * @return A list of VPN DNS servers.
*/
fun getVpnDnsServers(): List {
val vpnDns = MmkvManager.decodeSettingsString(AppConfig.PREF_VPN_DNS) ?: AppConfig.DNS_VPN
return vpnDns.split(",").filter { Utils.isPureIpAddress(it) }
- // allow empty, in that case dns will use system default
}
-
+ /**
+ * Get delay test URL.
+ * @param second Whether to use the second URL.
+ * @return The delay test URL.
+ */
fun getDelayTestUrl(second: Boolean = false): String {
return if (second) {
AppConfig.DelayTestUrl2
@@ -253,6 +326,10 @@ object SettingsManager {
}
}
+ /**
+ * Get the locale.
+ * @return The locale.
+ */
fun getLocale(): Locale {
val langCode =
MmkvManager.decodeSettingsString(AppConfig.PREF_LANGUAGE) ?: Language.AUTO.code
@@ -271,6 +348,9 @@ object SettingsManager {
}
}
+ /**
+ * Set night mode.
+ */
fun setNightMode() {
when (MmkvManager.decodeSettingsString(AppConfig.PREF_UI_MODE_NIGHT, "0")) {
"0" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SpeedtestManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SpeedtestManager.kt
index 2b72ed16..6ac62c20 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SpeedtestManager.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SpeedtestManager.kt
@@ -20,6 +20,13 @@ object SpeedtestManager {
private val tcpTestingSockets = ArrayList()
+ /**
+ * Measures the TCP connection time to a given URL and port.
+ *
+ * @param url The URL to connect to.
+ * @param port The port to connect to.
+ * @return The connection time in milliseconds, or -1 if the connection failed.
+ */
suspend fun tcping(url: String, port: Int): Long {
var time = -1L
for (k in 0 until 2) {
@@ -34,6 +41,12 @@ object SpeedtestManager {
return time
}
+ /**
+ * Measures the real ping time using the V2Ray library.
+ *
+ * @param config The configuration string for the V2Ray library.
+ * @return The ping time in milliseconds, or -1 if the ping failed.
+ */
fun realPing(config: String): Long {
return try {
Libv2ray.measureOutboundDelay(config, SettingsManager.getDelayTestUrl())
@@ -43,6 +56,12 @@ object SpeedtestManager {
}
}
+ /**
+ * Measures the ping time to a given URL using the system ping command.
+ *
+ * @param url The URL to ping.
+ * @return The ping time in milliseconds as a string, or "-1ms" if the ping failed.
+ */
fun ping(url: String): String {
try {
val command = "/system/bin/ping -c 3 $url"
@@ -62,6 +81,13 @@ object SpeedtestManager {
return "-1ms"
}
+ /**
+ * Measures the time taken to establish a TCP connection to a given URL and port.
+ *
+ * @param url The URL to connect to.
+ * @param port The port to connect to.
+ * @return The connection time in milliseconds, or -1 if the connection failed.
+ */
fun socketConnectTime(url: String, port: Int): Long {
try {
val socket = Socket()
@@ -86,6 +112,9 @@ object SpeedtestManager {
return -1
}
+ /**
+ * Closes all TCP sockets that are currently being tested.
+ */
fun closeAllTcpSockets() {
synchronized(this) {
tcpTestingSockets.forEach {
@@ -95,6 +124,13 @@ object SpeedtestManager {
}
}
+ /**
+ * Tests the connection to a given URL and port.
+ *
+ * @param context The Context in which the test is running.
+ * @param port The port to connect to.
+ * @return A pair containing the elapsed time in milliseconds and the result message.
+ */
fun testConnection(context: Context, port: Int): Pair {
var result: String
var elapsed = -1L
@@ -116,11 +152,9 @@ object SpeedtestManager {
)
}
} catch (e: IOException) {
- // network exception
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
Log.d(AppConfig.ANG_PACKAGE, "testConnection Exception: " + Log.getStackTraceString(e))
result = context.getString(R.string.connection_test_error, e.message)
} finally {
@@ -130,6 +164,11 @@ object SpeedtestManager {
return Pair(elapsed, result)
}
+ /**
+ * Gets the version of the V2Ray library.
+ *
+ * @return The version of the V2Ray library.
+ */
fun getLibVersion(): String {
return Libv2ray.checkVersionX()
}
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt
index 65321724..e0af5755 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt
@@ -53,6 +53,13 @@ import com.v2ray.ang.util.Utils
object V2rayConfigManager {
+ /**
+ * Retrieves the V2ray configuration for the given GUID.
+ *
+ * @param context The context of the caller.
+ * @param guid The unique identifier for the V2ray configuration.
+ * @return A ConfigResult object containing the configuration details or indicating failure.
+ */
fun getV2rayConfig(context: Context, guid: String): ConfigResult {
try {
val config = MmkvManager.decodeServerConfig(guid) ?: return ConfigResult(false)
@@ -72,6 +79,13 @@ object V2rayConfigManager {
}
}
+ /**
+ * Retrieves the non-custom V2ray configuration.
+ *
+ * @param context The context in which the function is called.
+ * @param config The profile item containing the configuration details.
+ * @return A ConfigResult object containing the result of the configuration retrieval.
+ */
private fun getV2rayNonCustomConfig(context: Context, config: ProfileItem): ConfigResult {
val result = ConfigResult(false)
@@ -630,6 +644,12 @@ object V2rayConfigManager {
return returnPair
}
+ /**
+ * Retrieves the proxy outbound configuration for the given profile item.
+ *
+ * @param profileItem The profile item for which to get the proxy outbound configuration.
+ * @return The proxy outbound configuration as a V2rayConfig.OutboundBean, or null if not found.
+ */
fun getProxyOutbound(profileItem: ProfileItem): V2rayConfig.OutboundBean? {
return when (profileItem.configType) {
EConfigType.VMESS -> VmessFmt.toOutbound(profileItem)
From 1a7ab97a3aacd992455f469ebf3b3eb7153e11c7 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Wed, 19 Mar 2025 12:08:19 +0800
Subject: [PATCH 063/238] Using AI to improve function documentation
---
.../main/java/com/v2ray/ang/extension/_Ext.kt | 64 +++++++++++++++++++
.../main/java/com/v2ray/ang/fmt/CustomFmt.kt | 6 ++
.../main/java/com/v2ray/ang/fmt/FmtBase.kt | 28 +++++++-
.../main/java/com/v2ray/ang/fmt/HttpFmt.kt | 8 ++-
.../java/com/v2ray/ang/fmt/Hysteria2Fmt.kt | 27 +++++++-
.../java/com/v2ray/ang/fmt/ShadowsocksFmt.kt | 32 +++++++++-
.../main/java/com/v2ray/ang/fmt/SocksFmt.kt | 19 +++++-
.../main/java/com/v2ray/ang/fmt/TrojanFmt.kt | 18 ++++++
.../main/java/com/v2ray/ang/fmt/VlessFmt.kt | 21 +++++-
.../main/java/com/v2ray/ang/fmt/VmessFmt.kt | 25 +++++++-
.../java/com/v2ray/ang/fmt/WireguardFmt.kt | 24 +++++++
11 files changed, 260 insertions(+), 12 deletions(-)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/extension/_Ext.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/extension/_Ext.kt
index a5e0da0b..824b1be8 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/extension/_Ext.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/extension/_Ext.kt
@@ -17,18 +17,38 @@ import java.net.URLConnection
val Context.v2RayApplication: AngApplication?
get() = applicationContext as? AngApplication
+/**
+ * Shows a toast message with the given resource ID.
+ *
+ * @param message The resource ID of the message to show.
+ */
fun Context.toast(message: Int) {
ToastCompat.makeText(this, message, Toast.LENGTH_SHORT).show()
}
+/**
+ * Shows a toast message with the given text.
+ *
+ * @param message The text of the message to show.
+ */
fun Context.toast(message: CharSequence) {
ToastCompat.makeText(this, message, Toast.LENGTH_SHORT).show()
}
+/**
+ * Puts a key-value pair into the JSONObject.
+ *
+ * @param pair The key-value pair to put.
+ */
fun JSONObject.putOpt(pair: Pair) {
put(pair.first, pair.second)
}
+/**
+ * Puts multiple key-value pairs into the JSONObject.
+ *
+ * @param pairs The map of key-value pairs to put.
+ */
fun JSONObject.putOpt(pairs: Map) {
pairs.forEach { put(it.key, it.value) }
}
@@ -36,8 +56,18 @@ fun JSONObject.putOpt(pairs: Map) {
const val THRESHOLD = 1000L
const val DIVISOR = 1024.0
+/**
+ * Converts a Long value to a speed string.
+ *
+ * @return The speed string.
+ */
fun Long.toSpeedString(): String = this.toTrafficString() + "/s"
+/**
+ * Converts a Long value to a traffic string.
+ *
+ * @return The traffic string.
+ */
fun Long.toTrafficString(): String {
val units = arrayOf("B", "KB", "MB", "GB", "TB", "PB")
var size = this.toDouble()
@@ -59,10 +89,27 @@ val URLConnection.responseLength: Long
val URI.idnHost: String
get() = host?.replace("[", "")?.replace("]", "").orEmpty()
+/**
+ * Removes all whitespace from the string.
+ *
+ * @return The string without whitespace.
+ */
fun String.removeWhiteSpace(): String = replace("\\s+".toRegex(), "")
+/**
+ * Converts the string to a Long value, or returns 0 if the conversion fails.
+ *
+ * @return The Long value.
+ */
fun String.toLongEx(): Long = toLongOrNull() ?: 0
+/**
+ * Listens for package changes and executes a callback when a change occurs.
+ *
+ * @param onetime Whether to unregister the receiver after the first callback.
+ * @param callback The callback to execute when a package change occurs.
+ * @return The BroadcastReceiver that was registered.
+ */
fun Context.listenForPackageChanges(onetime: Boolean = true, callback: () -> Unit) =
object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
@@ -85,14 +132,31 @@ fun Context.listenForPackageChanges(onetime: Boolean = true, callback: () -> Uni
}
}
+/**
+ * Retrieves a serializable object from the Bundle.
+ *
+ * @param key The key of the serializable object.
+ * @return The serializable object, or null if not found.
+ */
inline fun Bundle.serializable(key: String): T? = when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> getSerializable(key, T::class.java)
else -> @Suppress("DEPRECATION") getSerializable(key) as? T
}
+/**
+ * Retrieves a serializable object from the Intent.
+ *
+ * @param key The key of the serializable object.
+ * @return The serializable object, or null if not found.
+ */
inline fun Intent.serializable(key: String): T? = when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> getSerializableExtra(key, T::class.java)
else -> @Suppress("DEPRECATION") getSerializableExtra(key) as? T
}
+/**
+ * Checks if the CharSequence is not null and not empty.
+ *
+ * @return True if the CharSequence is not null and not empty, false otherwise.
+ */
fun CharSequence?.isNotNullEmpty(): Boolean = (this != null && this.isNotEmpty())
\ No newline at end of file
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/CustomFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/CustomFmt.kt
index 7a873832..3bc20927 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/CustomFmt.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/CustomFmt.kt
@@ -6,6 +6,12 @@ import com.v2ray.ang.dto.V2rayConfig
import com.v2ray.ang.util.JsonUtil
object CustomFmt : FmtBase() {
+ /**
+ * Parses a JSON string into a ProfileItem object.
+ *
+ * @param str the JSON string to parse
+ * @return the parsed ProfileItem object, or null if parsing fails
+ */
fun parse(str: String): ProfileItem? {
val config = ProfileItem.create(EConfigType.CUSTOM)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt
index 1ecfff47..0f6dbf1a 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt
@@ -8,6 +8,14 @@ import com.v2ray.ang.util.Utils
import java.net.URI
open class FmtBase {
+ /**
+ * Converts a ProfileItem object to a URI string.
+ *
+ * @param config the ProfileItem object to convert
+ * @param userInfo the user information to include in the URI
+ * @param dicQuery the query parameters to include in the URI
+ * @return the converted URI string
+ */
fun toUri(config: ProfileItem, userInfo: String?, dicQuery: HashMap?): String {
val query = if (dicQuery != null)
("?" + dicQuery.toList().joinToString(
@@ -25,11 +33,24 @@ open class FmtBase {
return "${url}${query}#${Utils.urlEncode(config.remarks)}"
}
+ /**
+ * Extracts query parameters from a URI.
+ *
+ * @param uri the URI to extract query parameters from
+ * @return a map of query parameters
+ */
fun getQueryParam(uri: URI): Map {
return uri.rawQuery.split("&")
.associate { it.split("=").let { (k, v) -> k to Utils.urlDecode(v) } }
}
+ /**
+ * Populates a ProfileItem object with values from query parameters.
+ *
+ * @param config the ProfileItem object to populate
+ * @param queryParam the query parameters to use for populating the ProfileItem
+ * @param allowInsecure whether to allow insecure connections
+ */
fun getItemFormQuery(config: ProfileItem, queryParam: Map, allowInsecure: Boolean) {
config.network = queryParam["type"] ?: NetworkType.TCP.type
config.headerType = queryParam["headerType"]
@@ -63,6 +84,12 @@ open class FmtBase {
config.flow = queryParam["flow"]
}
+ /**
+ * Creates a map of query parameters from a ProfileItem object.
+ *
+ * @param config the ProfileItem object to create query parameters from
+ * @return a map of query parameters
+ */
fun getQueryDic(config: ProfileItem): HashMap {
val dicQuery = HashMap()
dicQuery["security"] = config.security?.ifEmpty { "none" }.orEmpty()
@@ -121,5 +148,4 @@ open class FmtBase {
return dicQuery
}
-
}
\ No newline at end of file
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/HttpFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/HttpFmt.kt
index e9ebc53e..faac0230 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/HttpFmt.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/HttpFmt.kt
@@ -6,6 +6,12 @@ import com.v2ray.ang.dto.V2rayConfig.OutboundBean
import com.v2ray.ang.extension.isNotNullEmpty
object HttpFmt : FmtBase() {
+ /**
+ * Converts a ProfileItem object to an OutboundBean object.
+ *
+ * @param profileItem the ProfileItem object to convert
+ * @return the converted OutboundBean object, or null if conversion fails
+ */
fun toOutbound(profileItem: ProfileItem): OutboundBean? {
val outboundBean = OutboundBean.create(EConfigType.HTTP)
@@ -22,6 +28,4 @@ object HttpFmt : FmtBase() {
return outboundBean
}
-
-
}
\ No newline at end of file
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/Hysteria2Fmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/Hysteria2Fmt.kt
index 1c5dc99d..7a6abe08 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/Hysteria2Fmt.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/Hysteria2Fmt.kt
@@ -13,6 +13,12 @@ import com.v2ray.ang.util.Utils
import java.net.URI
object Hysteria2Fmt : FmtBase() {
+ /**
+ * Parses a Hysteria2 URI string into a ProfileItem object.
+ *
+ * @param str the Hysteria2 URI string to parse
+ * @return the parsed ProfileItem object, or null if parsing fails
+ */
fun parse(str: String): ProfileItem? {
var allowInsecure = MmkvManager.decodeSettingsBool(AppConfig.PREF_ALLOW_INSECURE, false)
val config = ProfileItem.create(EConfigType.HYSTERIA2)
@@ -45,6 +51,12 @@ object Hysteria2Fmt : FmtBase() {
return config
}
+ /**
+ * Converts a ProfileItem object to a URI string.
+ *
+ * @param config the ProfileItem object to convert
+ * @return the converted URI string
+ */
fun toUri(config: ProfileItem): String {
val dicQuery = HashMap()
@@ -67,6 +79,13 @@ object Hysteria2Fmt : FmtBase() {
return toUri(config, config.password, dicQuery)
}
+ /**
+ * Converts a ProfileItem object to a Hysteria2Bean object.
+ *
+ * @param config the ProfileItem object to convert
+ * @param socksPort the port number for the socks5 proxy
+ * @return the converted Hysteria2Bean object, or null if conversion fails
+ */
fun toNativeConfig(config: ProfileItem, socksPort: Int): Hysteria2Bean? {
val obfs = if (config.obfsPassword.isNullOrEmpty()) null else
@@ -118,10 +137,14 @@ object Hysteria2Fmt : FmtBase() {
return bean
}
-
+ /**
+ * Converts a ProfileItem object to an OutboundBean object.
+ *
+ * @param profileItem the ProfileItem object to convert
+ * @return the converted OutboundBean object, or null if conversion fails
+ */
fun toOutbound(profileItem: ProfileItem): OutboundBean? {
val outboundBean = OutboundBean.create(EConfigType.HYSTERIA2)
return outboundBean
}
-
}
\ No newline at end of file
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/ShadowsocksFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/ShadowsocksFmt.kt
index c4edc5bd..d32eeef7 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/ShadowsocksFmt.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/ShadowsocksFmt.kt
@@ -9,10 +9,22 @@ import com.v2ray.ang.util.Utils
import java.net.URI
object ShadowsocksFmt : FmtBase() {
+ /**
+ * Parses a Shadowsocks URI string into a ProfileItem object.
+ *
+ * @param str the Shadowsocks URI string to parse
+ * @return the parsed ProfileItem object, or null if parsing fails
+ */
fun parse(str: String): ProfileItem? {
return parseSip002(str) ?: parseLegacy(str)
}
+ /**
+ * Parses a SIP002 Shadowsocks URI string into a ProfileItem object.
+ *
+ * @param str the SIP002 Shadowsocks URI string to parse
+ * @return the parsed ProfileItem object, or null if parsing fails
+ */
fun parseSip002(str: String): ProfileItem? {
val config = ProfileItem.create(EConfigType.SHADOWSOCKS)
@@ -55,6 +67,12 @@ object ShadowsocksFmt : FmtBase() {
return config
}
+ /**
+ * Parses a legacy Shadowsocks URI string into a ProfileItem object.
+ *
+ * @param str the legacy Shadowsocks URI string to parse
+ * @return the parsed ProfileItem object, or null if parsing fails
+ */
fun parseLegacy(str: String): ProfileItem? {
val config = ProfileItem.create(EConfigType.SHADOWSOCKS)
var result = str.replace(EConfigType.SHADOWSOCKS.protocolScheme, "")
@@ -92,12 +110,24 @@ object ShadowsocksFmt : FmtBase() {
return config
}
+ /**
+ * Converts a ProfileItem object to a URI string.
+ *
+ * @param config the ProfileItem object to convert
+ * @return the converted URI string
+ */
fun toUri(config: ProfileItem): String {
val pw = "${config.method}:${config.password}"
return toUri(config, Utils.encode(pw), null)
}
+ /**
+ * Converts a ProfileItem object to an OutboundBean object.
+ *
+ * @param profileItem the ProfileItem object to convert
+ * @return the converted OutboundBean object, or null if conversion fails
+ */
fun toOutbound(profileItem: ProfileItem): OutboundBean? {
val outboundBean = OutboundBean.create(EConfigType.SHADOWSOCKS)
@@ -134,6 +164,4 @@ object ShadowsocksFmt : FmtBase() {
return outboundBean
}
-
-
}
\ No newline at end of file
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt
index 3b47bf3e..f37030c2 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt
@@ -9,6 +9,12 @@ import com.v2ray.ang.util.Utils
import java.net.URI
object SocksFmt : FmtBase() {
+ /**
+ * Parses a Socks URI string into a ProfileItem object.
+ *
+ * @param str the Socks URI string to parse
+ * @return the parsed ProfileItem object, or null if parsing fails
+ */
fun parse(str: String): ProfileItem? {
val config = ProfileItem.create(EConfigType.SOCKS)
@@ -31,6 +37,12 @@ object SocksFmt : FmtBase() {
return config
}
+ /**
+ * Converts a ProfileItem object to a URI string.
+ *
+ * @param config the ProfileItem object to convert
+ * @return the converted URI string
+ */
fun toUri(config: ProfileItem): String {
val pw =
if (config.username.isNotNullEmpty())
@@ -41,6 +53,12 @@ object SocksFmt : FmtBase() {
return toUri(config, Utils.encode(pw), null)
}
+ /**
+ * Converts a ProfileItem object to an OutboundBean object.
+ *
+ * @param profileItem the ProfileItem object to convert
+ * @return the converted OutboundBean object, or null if conversion fails
+ */
fun toOutbound(profileItem: ProfileItem): OutboundBean? {
val outboundBean = OutboundBean.create(EConfigType.SOCKS)
@@ -57,5 +75,4 @@ object SocksFmt : FmtBase() {
return outboundBean
}
-
}
\ No newline at end of file
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt
index db0c907d..56883cd8 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt
@@ -11,6 +11,12 @@ import com.v2ray.ang.util.Utils
import java.net.URI
object TrojanFmt : FmtBase() {
+ /**
+ * Parses a Trojan URI string into a ProfileItem object.
+ *
+ * @param str the Trojan URI string to parse
+ * @return the parsed ProfileItem object, or null if parsing fails
+ */
fun parse(str: String): ProfileItem? {
var allowInsecure = MmkvManager.decodeSettingsBool(AppConfig.PREF_ALLOW_INSECURE, false)
val config = ProfileItem.create(EConfigType.TROJAN)
@@ -35,12 +41,24 @@ object TrojanFmt : FmtBase() {
return config
}
+ /**
+ * Converts a ProfileItem object to a URI string.
+ *
+ * @param config the ProfileItem object to convert
+ * @return the converted URI string
+ */
fun toUri(config: ProfileItem): String {
val dicQuery = getQueryDic(config)
return toUri(config, config.password, dicQuery)
}
+ /**
+ * Converts a ProfileItem object to an OutboundBean object.
+ *
+ * @param profileItem the ProfileItem object to convert
+ * @return the converted OutboundBean object, or null if conversion fails
+ */
fun toOutbound(profileItem: ProfileItem): OutboundBean? {
val outboundBean = OutboundBean.create(EConfigType.TROJAN)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VlessFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VlessFmt.kt
index a4ca34c5..77d2f2a3 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VlessFmt.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VlessFmt.kt
@@ -12,6 +12,12 @@ import java.net.URI
object VlessFmt : FmtBase() {
+ /**
+ * Parses a Vless URI string into a ProfileItem object.
+ *
+ * @param str the Vless URI string to parse
+ * @return the parsed ProfileItem object, or null if parsing fails
+ */
fun parse(str: String): ProfileItem? {
var allowInsecure = MmkvManager.decodeSettingsBool(AppConfig.PREF_ALLOW_INSECURE, false)
val config = ProfileItem.create(EConfigType.VLESS)
@@ -31,6 +37,12 @@ object VlessFmt : FmtBase() {
return config
}
+ /**
+ * Converts a ProfileItem object to a URI string.
+ *
+ * @param config the ProfileItem object to convert
+ * @return the converted URI string
+ */
fun toUri(config: ProfileItem): String {
val dicQuery = getQueryDic(config)
dicQuery["encryption"] = config.method ?: "none"
@@ -38,7 +50,12 @@ object VlessFmt : FmtBase() {
return toUri(config, config.password, dicQuery)
}
-
+ /**
+ * Converts a ProfileItem object to an OutboundBean object.
+ *
+ * @param profileItem the ProfileItem object to convert
+ * @return the converted OutboundBean object, or null if conversion fails
+ */
fun toOutbound(profileItem: ProfileItem): OutboundBean? {
val outboundBean = OutboundBean.create(EConfigType.VLESS)
@@ -78,6 +95,4 @@ object VlessFmt : FmtBase() {
return outboundBean
}
-
-
}
\ No newline at end of file
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt
index e566f5fa..c5cba993 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt
@@ -16,6 +16,12 @@ import com.v2ray.ang.util.Utils
import java.net.URI
object VmessFmt : FmtBase() {
+ /**
+ * Parses a Vmess string into a ProfileItem object.
+ *
+ * @param str the Vmess string to parse
+ * @return the parsed ProfileItem object, or null if parsing fails
+ */
fun parse(str: String): ProfileItem? {
if (str.indexOf('?') > 0 && str.indexOf('&') > 0) {
return parseVmessStd(str)
@@ -79,6 +85,12 @@ object VmessFmt : FmtBase() {
return config
}
+ /**
+ * Converts a ProfileItem object to a URI string.
+ *
+ * @param config the ProfileItem object to convert
+ * @return the converted URI string
+ */
fun toUri(config: ProfileItem): String {
val vmessQRCode = VmessQRCode()
@@ -122,6 +134,12 @@ object VmessFmt : FmtBase() {
return Utils.encode(json)
}
+ /**
+ * Parses a standard Vmess URI string into a ProfileItem object.
+ *
+ * @param str the standard Vmess URI string to parse
+ * @return the parsed ProfileItem object, or null if parsing fails
+ */
fun parseVmessStd(str: String): ProfileItem? {
val allowInsecure = MmkvManager.decodeSettingsBool(AppConfig.PREF_ALLOW_INSECURE, false)
val config = ProfileItem.create(EConfigType.VMESS)
@@ -141,7 +159,12 @@ object VmessFmt : FmtBase() {
return config
}
-
+ /**
+ * Converts a ProfileItem object to an OutboundBean object.
+ *
+ * @param profileItem the ProfileItem object to convert
+ * @return the converted OutboundBean object, or null if conversion fails
+ */
fun toOutbound(profileItem: ProfileItem): OutboundBean? {
val outboundBean = OutboundBean.create(EConfigType.VMESS)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt
index 2bce4d24..2e6b0df8 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt
@@ -10,6 +10,12 @@ import com.v2ray.ang.util.Utils
import java.net.URI
object WireguardFmt : FmtBase() {
+ /**
+ * Parses a URI string into a ProfileItem object.
+ *
+ * @param str the URI string to parse
+ * @return the parsed ProfileItem object, or null if parsing fails
+ */
fun parse(str: String): ProfileItem? {
val config = ProfileItem.create(EConfigType.WIREGUARD)
@@ -31,6 +37,12 @@ object WireguardFmt : FmtBase() {
return config
}
+ /**
+ * Parses a Wireguard configuration file string into a ProfileItem object.
+ *
+ * @param str the Wireguard configuration file string to parse
+ * @return the parsed ProfileItem object, or null if parsing fails
+ */
fun parseWireguardConfFile(str: String): ProfileItem? {
val config = ProfileItem.create(EConfigType.WIREGUARD)
@@ -85,6 +97,12 @@ object WireguardFmt : FmtBase() {
return config
}
+ /**
+ * Converts a ProfileItem object to an OutboundBean object.
+ *
+ * @param profileItem the ProfileItem object to convert
+ * @return the converted OutboundBean object, or null if conversion fails
+ */
fun toOutbound(profileItem: ProfileItem): OutboundBean? {
val outboundBean = OutboundBean.create(EConfigType.WIREGUARD)
@@ -103,6 +121,12 @@ object WireguardFmt : FmtBase() {
return outboundBean
}
+ /**
+ * Converts a ProfileItem object to a URI string.
+ *
+ * @param config the ProfileItem object to convert
+ * @return the converted URI string
+ */
fun toUri(config: ProfileItem): String {
val dicQuery = HashMap()
From d0e8937f03df006fafd7473a80c82ade1e45b378 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Wed, 19 Mar 2025 12:15:43 +0800
Subject: [PATCH 064/238] Using AI to improve function documentation
---
.../main/java/com/v2ray/ang/AngApplication.kt | 16 ++--
.../com/v2ray/ang/viewmodel/MainViewModel.kt | 88 +++++++++++++++++--
.../v2ray/ang/viewmodel/SettingsViewModel.kt | 15 +++-
3 files changed, 99 insertions(+), 20 deletions(-)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/AngApplication.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/AngApplication.kt
index 13a31b2b..16e02f16 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/AngApplication.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/AngApplication.kt
@@ -10,10 +10,13 @@ import com.v2ray.ang.handler.SettingsManager
class AngApplication : MultiDexApplication() {
companion object {
- //const val PREF_LAST_VERSION = "pref_last_version"
lateinit var application: AngApplication
}
+ /**
+ * Attaches the base context to the application.
+ * @param base The base context.
+ */
override fun attachBaseContext(base: Context?) {
super.attachBaseContext(base)
application = this
@@ -23,16 +26,12 @@ class AngApplication : MultiDexApplication() {
.setDefaultProcessName("${ANG_PACKAGE}:bg")
.build()
+ /**
+ * Initializes the application.
+ */
override fun onCreate() {
super.onCreate()
-// LeakCanary.install(this)
-
-// val defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
-// firstRun = defaultSharedPreferences.getInt(PREF_LAST_VERSION, 0) != BuildConfig.VERSION_CODE
-// if (firstRun)
-// defaultSharedPreferences.edit().putInt(PREF_LAST_VERSION, BuildConfig.VERSION_CODE).apply()
-
MMKV.initialize(this)
SettingsManager.setNightMode()
@@ -41,5 +40,4 @@ class AngApplication : MultiDexApplication() {
SettingsManager.initRoutingRulesets(this)
}
-
}
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/MainViewModel.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/MainViewModel.kt
index a97d980b..852571ca 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/MainViewModel.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/MainViewModel.kt
@@ -49,7 +49,6 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
* 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
val mFilter = IntentFilter(AppConfig.BROADCAST_ACTION_ACTIVITY)
@@ -57,6 +56,9 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
MessageUtil.sendMsg2Service(getApplication(), AppConfig.MSG_REGISTER_CLIENT, "")
}
+ /**
+ * Called when the ViewModel is cleared.
+ */
override fun onCleared() {
getApplication().unregisterReceiver(mMsgReceiver)
tcpingTestScope.coroutineContext[Job]?.cancelChildren()
@@ -65,12 +67,19 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
super.onCleared()
}
+ /**
+ * Reloads the server list.
+ */
fun reloadServerList() {
serverList = MmkvManager.decodeServerList()
updateCache()
updateListAction.value = -1
}
+ /**
+ * Removes a server by its GUID.
+ * @param guid The GUID of the server to remove.
+ */
fun removeServer(guid: String) {
serverList.remove(guid)
MmkvManager.removeServer(guid)
@@ -80,6 +89,11 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
}
}
+ /**
+ * Appends a custom configuration server.
+ * @param server The server configuration to append.
+ * @return True if the server was successfully appended, false otherwise.
+ */
fun appendCustomConfigServer(server: String): Boolean {
if (server.contains("inbounds")
&& server.contains("outbounds")
@@ -107,6 +121,11 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
return false
}
+ /**
+ * Swaps the positions of two servers.
+ * @param fromPosition The initial position of the server.
+ * @param toPosition The target position of the server.
+ */
fun swapServer(fromPosition: Int, toPosition: Int) {
if (subscriptionId.isEmpty()) {
Collections.swap(serverList, fromPosition, toPosition)
@@ -119,6 +138,9 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
MmkvManager.encodeServerList(serverList)
}
+ /**
+ * Updates the cache of servers.
+ */
@Synchronized
fun updateCache() {
serversCache.clear()
@@ -147,6 +169,10 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
}
}
+ /**
+ * Updates the configuration via subscription for all servers.
+ * @return The number of updated configurations.
+ */
fun updateConfigViaSubAll(): Int {
if (subscriptionId.isEmpty()) {
return AngConfigManager.updateConfigViaSubAll()
@@ -156,6 +182,10 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
}
}
+ /**
+ * Exports all servers.
+ * @return The number of exported servers.
+ */
fun exportAllServer(): Int {
val serverListCopy =
if (subscriptionId.isEmpty() && keywordFilter.isEmpty()) {
@@ -171,14 +201,15 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
return ret
}
-
+ /**
+ * Tests the TCP ping for all servers.
+ */
fun testAllTcping() {
tcpingTestScope.coroutineContext[Job]?.cancelChildren()
SpeedtestManager.closeAllTcpSockets()
MmkvManager.clearAllTestDelayResults(serversCache.map { it.guid }.toList())
- //updateListAction.value = -1 // update all
- val serversCopy = serversCache.toList() // Create a copy of the list
+ val serversCopy = serversCache.toList()
for (item in serversCopy) {
item.profile.let { outbound ->
val serverAddress = outbound.server
@@ -196,23 +227,33 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
}
}
+ /**
+ * Tests the real ping for all servers.
+ */
fun testAllRealPing() {
MessageUtil.sendMsg2TestService(getApplication(), AppConfig.MSG_MEASURE_CONFIG_CANCEL, "")
MmkvManager.clearAllTestDelayResults(serversCache.map { it.guid }.toList())
- updateListAction.value = -1 // update all
+ updateListAction.value = -1
- val serversCopy = serversCache.toList() // Create a copy of the list
- viewModelScope.launch(Dispatchers.Default) { // without Dispatchers.Default viewModelScope will launch in main thread
+ val serversCopy = serversCache.toList()
+ viewModelScope.launch(Dispatchers.Default) {
for (item in serversCopy) {
MessageUtil.sendMsg2TestService(getApplication(), AppConfig.MSG_MEASURE_CONFIG, item.guid)
}
}
}
+ /**
+ * Tests the real ping for the current server.
+ */
fun testCurrentServerRealPing() {
MessageUtil.sendMsg2Service(getApplication(), AppConfig.MSG_MEASURE_DELAY, "")
}
+ /**
+ * Changes the subscription ID.
+ * @param id The new subscription ID.
+ */
fun subscriptionIdChanged(id: String) {
if (subscriptionId != id) {
subscriptionId = id
@@ -221,6 +262,11 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
}
}
+ /**
+ * Gets the subscriptions.
+ * @param context The context.
+ * @return A pair of lists containing the subscription IDs and remarks.
+ */
fun getSubscriptions(context: Context): Pair?, MutableList?> {
val subscriptions = MmkvManager.decodeSubscriptions()
if (subscriptionId.isNotEmpty()
@@ -239,6 +285,11 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
return listId to listRemarks
}
+ /**
+ * Gets the position of a server by its GUID.
+ * @param guid The GUID of the server.
+ * @return The position of the server.
+ */
fun getPosition(guid: String): Int {
serversCache.forEachIndexed { index, it ->
if (it.guid == guid)
@@ -247,6 +298,10 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
return -1
}
+ /**
+ * Removes duplicate servers.
+ * @return The number of removed servers.
+ */
fun removeDuplicateServer(): Int {
val serversCacheCopy = mutableListOf>()
for (it in serversCache) {
@@ -273,6 +328,10 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
return deleteServer.count()
}
+ /**
+ * Removes all servers.
+ * @return The number of removed servers.
+ */
fun removeAllServer(): Int {
val count =
if (subscriptionId.isEmpty() && keywordFilter.isEmpty()) {
@@ -287,6 +346,10 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
return count
}
+ /**
+ * Removes invalid servers.
+ * @return The number of removed servers.
+ */
fun removeInvalidServer(): Int {
var count = 0
if (subscriptionId.isEmpty() && keywordFilter.isEmpty()) {
@@ -300,6 +363,9 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
return count
}
+ /**
+ * Sorts servers by their test results.
+ */
fun sortByTestResults() {
data class ServerDelay(var guid: String, var testDelayMillis: Long)
@@ -319,12 +385,20 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
MmkvManager.encodeServerList(serverList)
}
+ /**
+ * Initializes assets.
+ * @param assets The asset manager.
+ */
fun initAssets(assets: AssetManager) {
viewModelScope.launch(Dispatchers.Default) {
SettingsManager.initAssets(getApplication(), assets)
}
}
+ /**
+ * Filters the configuration by a keyword.
+ * @param keyword The keyword to filter by.
+ */
fun filterConfig(keyword: String) {
if (keyword == keywordFilter) {
return
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/SettingsViewModel.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/SettingsViewModel.kt
index 96c92225..70db5ef8 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/SettingsViewModel.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/SettingsViewModel.kt
@@ -12,11 +12,17 @@ import com.v2ray.ang.handler.SettingsManager
class SettingsViewModel(application: Application) : AndroidViewModel(application),
SharedPreferences.OnSharedPreferenceChangeListener {
+ /**
+ * Starts listening for preference changes.
+ */
fun startListenPreferenceChange() {
PreferenceManager.getDefaultSharedPreferences(getApplication())
.registerOnSharedPreferenceChangeListener(this)
}
+ /**
+ * Called when the ViewModel is cleared.
+ */
override fun onCleared() {
PreferenceManager.getDefaultSharedPreferences(getApplication())
.unregisterOnSharedPreferenceChangeListener(this)
@@ -24,6 +30,11 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
super.onCleared()
}
+ /**
+ * Called when a shared preference is changed.
+ * @param sharedPreferences The shared preferences.
+ * @param key The key of the changed preference.
+ */
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String?) {
Log.d(AppConfig.ANG_PACKAGE, "Observe settings changed: $key")
when (key) {
@@ -77,10 +88,6 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
AppConfig.PREF_MUX_XUDP_CONCURRENCY -> {
MmkvManager.encodeSettings(key, sharedPreferences.getString(key, "8"))
}
-
-// AppConfig.PREF_PER_APP_PROXY_SET -> {
-// MmkvManager.encodeSettings(key, sharedPreferences.getStringSet(key, setOf()))
-// }
}
if (key == AppConfig.PREF_UI_MODE_NIGHT) {
SettingsManager.setNightMode()
From 12ab2954b0c0cc2aa7c239e599d522d07a55f5c2 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Wed, 19 Mar 2025 14:12:30 +0800
Subject: [PATCH 065/238] Using AI to improve function documentation
---
.../v2ray/ang/service/NotificationService.kt | 47 +++++++++++++--
.../com/v2ray/ang/service/ProcessService.kt | 8 +++
.../com/v2ray/ang/service/QSTileService.kt | 11 +++-
.../com/v2ray/ang/service/ServiceControl.kt | 15 +++++
.../v2ray/ang/service/SubscriptionUpdater.kt | 5 +-
.../ang/service/V2RayProxyOnlyService.kt | 37 ++++++++++++
.../v2ray/ang/service/V2RayServiceManager.kt | 60 ++++++++++++++++++-
.../com/v2ray/ang/service/V2RayTestService.kt | 20 +++++++
.../com/v2ray/ang/service/V2RayVpnService.kt | 25 ++++++--
9 files changed, 215 insertions(+), 13 deletions(-)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/NotificationService.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/NotificationService.kt
index 3b9fa40c..cb9ad811 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/NotificationService.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/NotificationService.kt
@@ -39,7 +39,10 @@ object NotificationService {
private var speedNotificationJob: Job? = null
private var mNotificationManager: NotificationManager? = null
-
+ /**
+ * Starts the speed notification.
+ * @param currentConfig The current profile configuration.
+ */
fun startSpeedNotification(currentConfig: ProfileItem?) {
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_SPEED_ENABLED) != true) return
if (speedNotificationJob != null || V2RayServiceManager.isRunning() == false) return
@@ -83,6 +86,10 @@ object NotificationService {
}
}
+ /**
+ * Shows the notification.
+ * @param currentConfig The current profile configuration.
+ */
fun showNotification(currentConfig: ProfileItem?) {
val service = getService() ?: return
val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
@@ -131,13 +138,15 @@ object NotificationService {
service.getString(R.string.title_service_restart),
restartV2RayPendingIntent
)
- //.build()
- //mBuilder?.setDefaults(NotificationCompat.FLAG_ONLY_ALERT_ONCE) //取消震动,铃声其他都不好使
+ //mBuilder?.setDefaults(NotificationCompat.FLAG_ONLY_ALERT_ONCE)
service.startForeground(NOTIFICATION_ID, mBuilder?.build())
}
+ /**
+ * Cancels the notification.
+ */
fun cancelNotification() {
val service = getService() ?: return
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
@@ -151,6 +160,10 @@ object NotificationService {
speedNotificationJob = null
}
+ /**
+ * Stops the speed notification.
+ * @param currentConfig The current profile configuration.
+ */
fun stopSpeedNotification(currentConfig: ProfileItem?) {
speedNotificationJob?.let {
it.cancel()
@@ -159,6 +172,10 @@ object NotificationService {
}
}
+ /**
+ * Creates a notification channel for Android O and above.
+ * @return The channel ID.
+ */
@RequiresApi(Build.VERSION_CODES.O)
private fun createNotificationChannel(): String {
val channelId = AppConfig.RAY_NG_CHANNEL_ID
@@ -174,6 +191,12 @@ object NotificationService {
return channelId
}
+ /**
+ * Updates the notification with the given content text and traffic data.
+ * @param contentText The content text.
+ * @param proxyTraffic The proxy traffic.
+ * @param directTraffic The direct traffic.
+ */
private fun updateNotification(contentText: String?, proxyTraffic: Long, directTraffic: Long) {
if (mBuilder != null) {
if (proxyTraffic < NOTIFICATION_ICON_THRESHOLD && directTraffic < NOTIFICATION_ICON_THRESHOLD) {
@@ -184,11 +207,15 @@ object NotificationService {
mBuilder?.setSmallIcon(R.drawable.ic_stat_direct)
}
mBuilder?.setStyle(NotificationCompat.BigTextStyle().bigText(contentText))
- mBuilder?.setContentText(contentText) // Emui4.1 need content text even if style is set as BigTextStyle
+ mBuilder?.setContentText(contentText)
getNotificationManager()?.notify(NOTIFICATION_ID, mBuilder?.build())
}
}
+ /**
+ * Gets the notification manager.
+ * @return The notification manager.
+ */
private fun getNotificationManager(): NotificationManager? {
if (mNotificationManager == null) {
val service = getService() ?: return null
@@ -197,6 +224,13 @@ object NotificationService {
return mNotificationManager
}
+ /**
+ * Appends the speed string to the given text.
+ * @param text The text to append to.
+ * @param name The name of the tag.
+ * @param up The uplink speed.
+ * @param down The downlink speed.
+ */
private fun appendSpeedString(text: StringBuilder, name: String?, up: Double, down: Double) {
var n = name ?: "no tag"
n = n.substring(0, min(n.length, 6))
@@ -207,8 +241,11 @@ object NotificationService {
text.append("• ${up.toLong().toSpeedString()}↑ ${down.toLong().toSpeedString()}↓\n")
}
+ /**
+ * Gets the service instance.
+ * @return The service instance.
+ */
private fun getService(): Service? {
return V2RayServiceManager.serviceControl?.get()?.getService()
}
-
}
\ No newline at end of file
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/ProcessService.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/ProcessService.kt
index 89ab476f..fb8daa99 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/ProcessService.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/ProcessService.kt
@@ -10,6 +10,11 @@ import kotlinx.coroutines.launch
class ProcessService {
private var process: Process? = null
+ /**
+ * Runs a process with the given command.
+ * @param context The context.
+ * @param cmd The command to run.
+ */
fun runProcess(context: Context, cmd: MutableList) {
Log.d(ANG_PACKAGE, cmd.toString())
@@ -33,6 +38,9 @@ class ProcessService {
}
}
+ /**
+ * Stops the running process.
+ */
fun stopProcess() {
try {
Log.d(ANG_PACKAGE, "runProcess destroy")
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/QSTileService.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/QSTileService.kt
index e5d0dec5..87931874 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/QSTileService.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/QSTileService.kt
@@ -19,6 +19,10 @@ import java.lang.ref.SoftReference
@TargetApi(Build.VERSION_CODES.N)
class QSTileService : TileService() {
+ /**
+ * Sets the state of the tile.
+ * @param state The state to set.
+ */
fun setState(state: Int) {
if (state == Tile.STATE_INACTIVE) {
qsTile?.state = Tile.STATE_INACTIVE
@@ -37,7 +41,6 @@ class QSTileService : TileService() {
* 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()
@@ -48,6 +51,9 @@ class QSTileService : TileService() {
MessageUtil.sendMsg2Service(this, AppConfig.MSG_REGISTER_CLIENT, "")
}
+ /**
+ * Called when the tile stops listening.
+ */
override fun onStopListening() {
super.onStopListening()
@@ -60,6 +66,9 @@ class QSTileService : TileService() {
}
+ /**
+ * Called when the tile is clicked.
+ */
override fun onClick() {
super.onClick()
when (qsTile.state) {
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/ServiceControl.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/ServiceControl.kt
index 36a26b88..085c72eb 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/ServiceControl.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/ServiceControl.kt
@@ -3,11 +3,26 @@ package com.v2ray.ang.service
import android.app.Service
interface ServiceControl {
+ /**
+ * Gets the service instance.
+ * @return The service instance.
+ */
fun getService(): Service
+ /**
+ * Starts the service.
+ */
fun startService()
+ /**
+ * Stops the service.
+ */
fun stopService()
+ /**
+ * Protects the VPN socket.
+ * @param socket The socket to protect.
+ * @return True if the socket is protected, false otherwise.
+ */
fun vpnProtect(socket: Int): Boolean
}
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/SubscriptionUpdater.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/SubscriptionUpdater.kt
index 4de3381b..0c6ed03e 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/SubscriptionUpdater.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/SubscriptionUpdater.kt
@@ -19,7 +19,6 @@ import com.v2ray.ang.handler.MmkvManager
object SubscriptionUpdater {
-
class UpdateTask(context: Context, params: WorkerParameters) :
CoroutineWorker(context, params) {
@@ -33,6 +32,10 @@ object SubscriptionUpdater {
.setCategory(NotificationCompat.CATEGORY_SERVICE)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
+ /**
+ * Performs the subscription update work.
+ * @return The result of the work.
+ */
@SuppressLint("MissingPermission")
override suspend fun doWork(): Result {
Log.d(AppConfig.ANG_PACKAGE, "subscription automatic update starting")
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayProxyOnlyService.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayProxyOnlyService.kt
index 589e2c6f..66d74e86 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayProxyOnlyService.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayProxyOnlyService.kt
@@ -11,41 +11,78 @@ import com.v2ray.ang.util.MyContextWrapper
import java.lang.ref.SoftReference
class V2RayProxyOnlyService : Service(), ServiceControl {
+ /**
+ * Initializes the service.
+ */
override fun onCreate() {
super.onCreate()
V2RayServiceManager.serviceControl = SoftReference(this)
}
+ /**
+ * Handles the start command for the service.
+ * @param intent The intent.
+ * @param flags The flags.
+ * @param startId The start ID.
+ * @return The start mode.
+ */
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
V2RayServiceManager.startV2rayPoint()
return START_STICKY
}
+ /**
+ * Destroys the service.
+ */
override fun onDestroy() {
super.onDestroy()
V2RayServiceManager.stopV2rayPoint()
}
+ /**
+ * Gets the service instance.
+ * @return The service instance.
+ */
override fun getService(): Service {
return this
}
+ /**
+ * Starts the service.
+ */
override fun startService() {
// do nothing
}
+ /**
+ * Stops the service.
+ */
override fun stopService() {
stopSelf()
}
+ /**
+ * Protects the VPN socket.
+ * @param socket The socket to protect.
+ * @return True if the socket is protected, false otherwise.
+ */
override fun vpnProtect(socket: Int): Boolean {
return true
}
+ /**
+ * Binds the service.
+ * @param intent The intent.
+ * @return The binder.
+ */
override fun onBind(intent: Intent?): IBinder? {
return null
}
+ /**
+ * Attaches the base context to the service.
+ * @param newBase The new base context.
+ */
@RequiresApi(Build.VERSION_CODES.N)
override fun attachBaseContext(newBase: Context?) {
val context = newBase?.let {
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt
index 3d36d24e..48fef837 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayServiceManager.kt
@@ -42,6 +42,11 @@ object V2RayServiceManager {
Libv2ray.initV2Env(Utils.userAssetPath(value?.get()?.getService()), Utils.getDeviceIdForXUDPBaseKey())
}
+ /**
+ * Starts the V2Ray service from a toggle action.
+ * @param context The context from which the service is started.
+ * @return True if the service was started successfully, false otherwise.
+ */
fun startVServiceFromToggle(context: Context): Boolean {
if (MmkvManager.getSelectServer().isNullOrEmpty()) {
context.toast(R.string.app_tile_first_use)
@@ -51,6 +56,11 @@ object V2RayServiceManager {
return true
}
+ /**
+ * Starts the V2Ray service.
+ * @param context The context from which the service is started.
+ * @param guid The GUID of the server configuration to use (optional).
+ */
fun startVService(context: Context, guid: String? = null) {
if (guid != null) {
MmkvManager.setSelectServer(guid)
@@ -58,15 +68,31 @@ object V2RayServiceManager {
startContextService(context)
}
+ /**
+ * Stops the V2Ray service.
+ * @param context The context from which the service is stopped.
+ */
fun stopVService(context: Context) {
context.toast(R.string.toast_services_stop)
MessageUtil.sendMsg2Service(context, AppConfig.MSG_STATE_STOP, "")
}
+ /**
+ * Checks if the V2Ray service is running.
+ * @return True if the service is running, false otherwise.
+ */
fun isRunning() = v2rayPoint.isRunning
+ /**
+ * Gets the name of the currently running server.
+ * @return The name of the running server.
+ */
fun getRunningServerName() = currentConfig?.remarks.orEmpty()
+ /**
+ * Starts the context service for V2Ray.
+ * @param context The context from which the service is started.
+ */
private fun startContextService(context: Context) {
if (v2rayPoint.isRunning) return
val guid = MmkvManager.getSelectServer() ?: return
@@ -98,8 +124,8 @@ 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)`.
+ * Starts the V2Ray point.
*/
-
fun startV2rayPoint() {
val service = getService() ?: return
val guid = MmkvManager.getSelectServer() ?: return
@@ -142,6 +168,9 @@ object V2RayServiceManager {
}
}
+ /**
+ * Stops the V2Ray point.
+ */
fun stopV2rayPoint() {
val service = getService() ?: return
@@ -166,10 +195,19 @@ object V2RayServiceManager {
PluginUtil.stopPlugin()
}
+ /**
+ * Queries the statistics for a given tag and link.
+ * @param tag The tag to query.
+ * @param link The link to query.
+ * @return The statistics value.
+ */
fun queryStats(tag: String, link: String): Long {
return v2rayPoint.queryStats(tag, link)
}
+ /**
+ * Measures the delay for V2Ray.
+ */
private fun measureV2rayDelay() {
CoroutineScope(Dispatchers.IO).launch {
val service = getService() ?: return@launch
@@ -201,6 +239,10 @@ object V2RayServiceManager {
}
}
+ /**
+ * Gets the current service instance.
+ * @return The current service instance, or null if not available.
+ */
private fun getService(): Service? {
return serviceControl?.get()?.getService()
}
@@ -227,10 +269,21 @@ object V2RayServiceManager {
return serviceControl.vpnProtect(l.toInt())
}
+ /**
+ * Called by Go to emit status.
+ * @param l The status code.
+ * @param s The status message.
+ * @return The status code.
+ */
override fun onEmitStatus(l: Long, s: String?): Long {
return 0
}
+ /**
+ * Called by Go to set up the service.
+ * @param s The setup string.
+ * @return The status code.
+ */
override fun setup(s: String): Long {
val serviceControl = serviceControl?.get() ?: return -1
return try {
@@ -245,6 +298,11 @@ object V2RayServiceManager {
}
private class ReceiveMessageHandler : BroadcastReceiver() {
+ /**
+ * Handles received broadcast messages.
+ * @param ctx The context in which the receiver is running.
+ * @param intent The intent being received.
+ */
override fun onReceive(ctx: Context?, intent: Intent?) {
val serviceControl = serviceControl?.get() ?: return
when (intent?.getIntExtra("key", 0)) {
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayTestService.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayTestService.kt
index 4e546d58..8bdbc6d1 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayTestService.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayTestService.kt
@@ -26,12 +26,22 @@ import java.util.concurrent.Executors
class V2RayTestService : Service() {
private val realTestScope by lazy { CoroutineScope(Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()).asCoroutineDispatcher()) }
+ /**
+ * Initializes the V2Ray environment.
+ */
override fun onCreate() {
super.onCreate()
Seq.setContext(this)
Libv2ray.initV2Env(Utils.userAssetPath(this), Utils.getDeviceIdForXUDPBaseKey())
}
+ /**
+ * Handles the start command for the service.
+ * @param intent The intent.
+ * @param flags The flags.
+ * @param startId The start ID.
+ * @return The start mode.
+ */
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
when (intent?.getIntExtra("key", 0)) {
MSG_MEASURE_CONFIG -> {
@@ -49,10 +59,20 @@ class V2RayTestService : Service() {
return super.onStartCommand(intent, flags, startId)
}
+ /**
+ * Binds the service.
+ * @param intent The intent.
+ * @return The binder.
+ */
override fun onBind(intent: Intent?): IBinder? {
return null
}
+ /**
+ * Starts the real ping test.
+ * @param guid The GUID of the configuration.
+ * @return The ping result.
+ */
private fun startRealPing(guid: String): Long {
val retFailure = -1L
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayVpnService.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayVpnService.kt
index 11b12668..69c1adb6 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayVpnService.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayVpnService.kt
@@ -42,11 +42,8 @@ class V2RayVpnService : VpnService(), ServiceControl {
private const val TUN2SOCKS = "libtun2socks.so"
}
-
private lateinit var mInterface: ParcelFileDescriptor
private var isRunning = false
-
- //val fd: Int get() = mInterface.fd
private lateinit var process: Process
/**destroy
@@ -88,7 +85,6 @@ class V2RayVpnService : VpnService(), ServiceControl {
override fun onCreate() {
super.onCreate()
-
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
StrictMode.setThreadPolicy(policy)
V2RayServiceManager.serviceControl = SoftReference(this)
@@ -138,6 +134,10 @@ class V2RayVpnService : VpnService(), ServiceControl {
super.attachBaseContext(context)
}
+ /**
+ * Sets up the VPN service.
+ * Prepares the VPN and configures it if preparation is successful.
+ */
private fun setup() {
val prepare = prepare(this)
if (prepare != null) {
@@ -151,6 +151,10 @@ class V2RayVpnService : VpnService(), ServiceControl {
runTun2socks()
}
+ /**
+ * Configures the VPN service.
+ * @return True if the VPN service was configured successfully, false otherwise.
+ */
private fun setupVpnService(): Boolean {
// If the old interface has exactly the same parameters, use it!
// Configure a builder while parsing the parameters.
@@ -247,6 +251,10 @@ class V2RayVpnService : VpnService(), ServiceControl {
return false
}
+ /**
+ * Runs the tun2socks process.
+ * Starts the tun2socks process with the appropriate parameters.
+ */
private fun runTun2socks() {
val socksPort = SettingsManager.getSocksPort()
val cmd = arrayListOf(
@@ -294,6 +302,10 @@ class V2RayVpnService : VpnService(), ServiceControl {
}
}
+ /**
+ * Sends the file descriptor to the tun2socks process.
+ * Attempts to send the file descriptor multiple times if necessary.
+ */
private fun sendFd() {
val fd = mInterface.fileDescriptor
val path = File(applicationContext.filesDir, "sock_path").absolutePath
@@ -318,6 +330,10 @@ class V2RayVpnService : VpnService(), ServiceControl {
}
}
+ /**
+ * Stops the V2Ray service.
+ * @param isForced Whether to force stop the service.
+ */
private fun stopV2Ray(isForced: Boolean = true) {
// val configName = defaultDPreference.getPrefString(PREF_CURR_CONFIG_GUID, "")
// val emptyInfo = VpnNetworkInfo()
@@ -356,5 +372,4 @@ class V2RayVpnService : VpnService(), ServiceControl {
}
}
}
-
}
From 28b1788dc10e205bf6603291bda3cfbdb524079d Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Wed, 19 Mar 2025 20:21:12 +0800
Subject: [PATCH 066/238] Remove the use of androidx.cardview.widget.CardView
in the list
---
.../src/main/java/com/v2ray/ang/AppConfig.kt | 1 -
.../ang/helper/CustomDividerItemDecoration.kt | 68 ++++
.../java/com/v2ray/ang/ui/BaseActivity.kt | 25 ++
.../java/com/v2ray/ang/ui/LogcatActivity.kt | 3 +-
.../java/com/v2ray/ang/ui/MainActivity.kt | 1 +
.../com/v2ray/ang/ui/MainRecyclerAdapter.kt | 1 -
.../com/v2ray/ang/ui/PerAppProxyActivity.kt | 3 +-
.../v2ray/ang/ui/RoutingSettingActivity.kt | 1 +
.../com/v2ray/ang/ui/SubSettingActivity.kt | 1 +
.../com/v2ray/ang/ui/UserAssetActivity.kt | 1 +
.../src/main/res/drawable/custom_divider.xml | 13 +
.../main/res/layout/activity_bypass_list.xml | 4 +-
.../app/src/main/res/layout/activity_main.xml | 2 +-
.../res/layout/activity_routing_setting.xml | 37 +--
.../res/layout/item_recycler_bypass_list.xml | 26 +-
.../main/res/layout/item_recycler_logcat.xml | 6 +-
.../main/res/layout/item_recycler_main.xml | 303 +++++++++---------
.../layout/item_recycler_routing_setting.xml | 175 +++++-----
.../res/layout/item_recycler_sub_setting.xml | 164 +++++-----
.../res/layout/item_recycler_user_asset.xml | 130 ++++----
.../app/src/main/res/values-night/colors.xml | 1 +
V2rayNG/app/src/main/res/values/colors.xml | 1 +
V2rayNG/app/src/main/res/values/dimens.xml | 2 +-
23 files changed, 525 insertions(+), 444 deletions(-)
create mode 100644 V2rayNG/app/src/main/java/com/v2ray/ang/helper/CustomDividerItemDecoration.kt
create mode 100644 V2rayNG/app/src/main/res/drawable/custom_divider.xml
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/AppConfig.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/AppConfig.kt
index 2b422985..ba45042c 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/AppConfig.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/AppConfig.kt
@@ -12,7 +12,6 @@ object AppConfig {
/** Legacy configuration keys. */
const val ANG_CONFIG = "ang_config"
- const val PREF_INAPP_BUY_IS_PREMIUM = "pref_inapp_buy_is_premium"
/** Preferences mapped to MMKV storage. */
const val PREF_SNIFFING_ENABLED = "pref_sniffing_enabled"
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/helper/CustomDividerItemDecoration.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/helper/CustomDividerItemDecoration.kt
new file mode 100644
index 00000000..0f6d37c0
--- /dev/null
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/helper/CustomDividerItemDecoration.kt
@@ -0,0 +1,68 @@
+package com.v2ray.ang.helper
+
+import android.graphics.Canvas
+import android.graphics.Rect
+import android.graphics.drawable.Drawable
+import android.view.View
+import androidx.recyclerview.widget.RecyclerView
+
+class CustomDividerItemDecoration(
+ private val divider: Drawable,
+ private val orientation: Int
+) : RecyclerView.ItemDecoration() {
+
+ override fun onDraw(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {
+ if (orientation == RecyclerView.VERTICAL) {
+ drawVerticalDividers(canvas, parent)
+ } else {
+ drawHorizontalDividers(canvas, parent)
+ }
+ }
+
+ private fun drawVerticalDividers(canvas: Canvas, parent: RecyclerView) {
+ val left = parent.paddingLeft
+ val right = parent.width - parent.paddingRight
+
+ val childCount = parent.childCount
+ for (i in 0 until childCount - 1) {
+ val child = parent.getChildAt(i)
+ val params = child.layoutParams as RecyclerView.LayoutParams
+
+ val top = child.bottom + params.bottomMargin
+ val bottom = top + divider.intrinsicHeight
+
+ divider.setBounds(left, top, right, bottom)
+ divider.draw(canvas)
+ }
+ }
+
+ private fun drawHorizontalDividers(canvas: Canvas, parent: RecyclerView) {
+ val top = parent.paddingTop
+ val bottom = parent.height - parent.paddingBottom
+
+ val childCount = parent.childCount
+ for (i in 0 until childCount - 1) {
+ val child = parent.getChildAt(i)
+ val params = child.layoutParams as RecyclerView.LayoutParams
+
+ val left = child.right + params.rightMargin
+ val right = left + divider.intrinsicWidth
+
+ divider.setBounds(left, top, right, bottom)
+ divider.draw(canvas)
+ }
+ }
+
+ override fun getItemOffsets(
+ outRect: Rect,
+ view: View,
+ parent: RecyclerView,
+ state: RecyclerView.State
+ ) {
+ if (orientation == RecyclerView.VERTICAL) {
+ outRect.set(0, 0, 0, divider.intrinsicHeight)
+ } else {
+ outRect.set(0, 0, divider.intrinsicWidth, 0)
+ }
+ }
+}
\ No newline at end of file
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/BaseActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/BaseActivity.kt
index 46287ceb..0cd7f647 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/BaseActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/BaseActivity.kt
@@ -6,11 +6,16 @@ import android.os.Bundle
import android.view.MenuItem
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
+import androidx.core.content.ContextCompat
import androidx.core.view.WindowCompat
+import androidx.recyclerview.widget.DividerItemDecoration
+import androidx.recyclerview.widget.RecyclerView
import com.v2ray.ang.handler.SettingsManager
+import com.v2ray.ang.helper.CustomDividerItemDecoration
import com.v2ray.ang.util.MyContextWrapper
import com.v2ray.ang.util.Utils
+
abstract class BaseActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -37,4 +42,24 @@ abstract class BaseActivity : AppCompatActivity() {
override fun attachBaseContext(newBase: Context?) {
super.attachBaseContext(MyContextWrapper.wrap(newBase ?: return, SettingsManager.getLocale()))
}
+
+ /**
+ * Adds a custom divider to a RecyclerView.
+ *
+ * @param recyclerView The target RecyclerView to which the divider will be added.
+ * @param context The context used to access resources.
+ * @param drawableResId The resource ID of the drawable to be used as the divider.
+ * @param orientation The orientation of the divider (DividerItemDecoration.VERTICAL or DividerItemDecoration.HORIZONTAL).
+ */
+ fun addCustomDividerToRecyclerView(recyclerView: RecyclerView, context: Context?, drawableResId: Int, orientation: Int = DividerItemDecoration.VERTICAL) {
+ // Get the drawable from resources
+ val drawable = ContextCompat.getDrawable(context!!, drawableResId)
+ requireNotNull(drawable) { "Drawable resource not found" }
+
+ // Create a DividerItemDecoration with the specified orientation
+ val dividerItemDecoration = CustomDividerItemDecoration(drawable, orientation)
+
+ // Add the divider to the RecyclerView
+ recyclerView.addItemDecoration(dividerItemDecoration)
+ }
}
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/LogcatActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/LogcatActivity.kt
index b275c348..3ee5b399 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/LogcatActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/LogcatActivity.kt
@@ -5,7 +5,6 @@ import android.view.Menu
import android.view.MenuItem
import androidx.appcompat.widget.SearchView
import androidx.lifecycle.lifecycleScope
-import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.v2ray.ang.AppConfig.ANG_PACKAGE
@@ -34,8 +33,8 @@ class LogcatActivity : BaseActivity(), SwipeRefreshLayout.OnRefreshListener {
binding.recyclerView.setHasFixedSize(true)
binding.recyclerView.layoutManager = LinearLayoutManager(this)
+ addCustomDividerToRecyclerView(binding.recyclerView, this, R.drawable.custom_divider)
binding.recyclerView.adapter = adapter
- binding.recyclerView.addItemDecoration(DividerItemDecoration(this, DividerItemDecoration.VERTICAL))
binding.refreshLayout.setOnRefreshListener(this)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt
index 6d441abf..25618841 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt
@@ -163,6 +163,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
binding.recyclerView.setHasFixedSize(true)
binding.recyclerView.layoutManager = LinearLayoutManager(this)
+ addCustomDividerToRecyclerView(binding.recyclerView, this, R.drawable.custom_divider)
binding.recyclerView.adapter = adapter
mItemTouchHelper = ItemTouchHelper(SimpleItemTouchHelperCallback(adapter))
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainRecyclerAdapter.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainRecyclerAdapter.kt
index 54cdecf8..f1b2cb82 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainRecyclerAdapter.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainRecyclerAdapter.kt
@@ -178,7 +178,6 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter
+
+ -
+
+
+
+
+
+
\ No newline at end of file
diff --git a/V2rayNG/app/src/main/res/layout/activity_bypass_list.xml b/V2rayNG/app/src/main/res/layout/activity_bypass_list.xml
index 2665430a..5c23332d 100644
--- a/V2rayNG/app/src/main/res/layout/activity_bypass_list.xml
+++ b/V2rayNG/app/src/main/res/layout/activity_bypass_list.xml
@@ -41,6 +41,7 @@
android:maxLines="2"
android:text="@string/title_pref_per_app_proxy"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
+ android:textColor="@color/colorAccent"
app:theme="@style/BrandedSwitch" />
@@ -60,6 +61,7 @@
android:layout_height="wrap_content"
android:text="@string/switch_bypass_apps_mode"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
+ android:textColor="@color/colorAccent"
app:theme="@style/BrandedSwitch" />
@@ -77,9 +79,9 @@
diff --git a/V2rayNG/app/src/main/res/layout/activity_main.xml b/V2rayNG/app/src/main/res/layout/activity_main.xml
index 9e0ae6e0..71e1c527 100644
--- a/V2rayNG/app/src/main/res/layout/activity_main.xml
+++ b/V2rayNG/app/src/main/res/layout/activity_main.xml
@@ -47,7 +47,7 @@
diff --git a/V2rayNG/app/src/main/res/layout/activity_routing_setting.xml b/V2rayNG/app/src/main/res/layout/activity_routing_setting.xml
index 825c1c50..3032d6ce 100644
--- a/V2rayNG/app/src/main/res/layout/activity_routing_setting.xml
+++ b/V2rayNG/app/src/main/res/layout/activity_routing_setting.xml
@@ -17,41 +17,32 @@
android:layout_height="wrap_content"
android:orientation="vertical">
-
+ android:layout_margin="@dimen/layout_margin_top_height"
+ android:orientation="vertical">
-
+ android:text="@string/routing_settings_domain_strategy" />
-
+
+
-
-
-
+ android:orientation="vertical"
+ android:paddingStart="@dimen/padding_start">
+ android:focusable="true"
+ android:gravity="center_vertical">
+ android:layout_width="@dimen/server_height"
+ android:layout_height="@dimen/server_height"
+ android:padding="@dimen/padding" />
+ android:orientation="vertical">
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
+ android:maxLines="1" />
-
+ android:padding="@dimen/padding"/>
\ No newline at end of file
diff --git a/V2rayNG/app/src/main/res/layout/item_recycler_logcat.xml b/V2rayNG/app/src/main/res/layout/item_recycler_logcat.xml
index 927a79ed..fb1b6827 100644
--- a/V2rayNG/app/src/main/res/layout/item_recycler_logcat.xml
+++ b/V2rayNG/app/src/main/res/layout/item_recycler_logcat.xml
@@ -9,9 +9,11 @@
android:orientation="vertical">
+ android:layout_margin="@dimen/layout_margin_spacing"
+ android:paddingTop="@dimen/layout_margin_spacing"
+ android:paddingBottom="@dimen/layout_margin_spacing">
-
+ android:layout_height="@dimen/server_height"
+ android:layout_gravity="center"
+ android:layout_margin="@dimen/layout_margin_spacing"
+ android:background="?attr/selectableItemBackground"
+ android:clickable="true"
+ android:focusable="true"
+ android:gravity="center"
+ android:nextFocusRight="@+id/layout_share"
+ android:orientation="horizontal">
+ android:orientation="vertical" />
+
+
-
-
+ android:orientation="vertical">
-
+ android:maxLines="2"
+ android:minLines="1"
+ android:textAppearance="@style/TextAppearance.AppCompat.Subhead" />
-
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ android:layout_height="@dimen/png_height"
+ app:srcCompat="@drawable/ic_share_24dp" />
+
+
+
+
+
+
+
+
+
+
+
+ android:layout_gravity="right"
+ android:orientation="vertical"
+ android:paddingBottom="4dp"
+ android:paddingEnd="@dimen/padding_end">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ android:lines="1"
+ android:textAppearance="@style/TextAppearance.AppCompat.Small"
+ android:textColor="@color/colorConfigType"
+ android:textSize="10sp" />
-
-
+
+
+
diff --git a/V2rayNG/app/src/main/res/layout/item_recycler_routing_setting.xml b/V2rayNG/app/src/main/res/layout/item_recycler_routing_setting.xml
index daf1020f..71976d0f 100644
--- a/V2rayNG/app/src/main/res/layout/item_recycler_routing_setting.xml
+++ b/V2rayNG/app/src/main/res/layout/item_recycler_routing_setting.xml
@@ -6,113 +6,108 @@
android:layout_height="wrap_content"
android:gravity="center_vertical">
-
+ android:paddingTop="@dimen/layout_margin_spacing"
+ android:paddingBottom="@dimen/layout_margin_spacing">
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:paddingStart="@dimen/padding_start">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+ android:padding="@dimen/layout_margin_spacing">
+
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/layout_margin_spacing"
+ android:orientation="horizontal">
-
-
-
-
-
-
-
-
-
+ app:theme="@style/BrandedSwitch" />
-
-
+
+
+
diff --git a/V2rayNG/app/src/main/res/layout/item_recycler_sub_setting.xml b/V2rayNG/app/src/main/res/layout/item_recycler_sub_setting.xml
index 663c53ab..a2e335be 100644
--- a/V2rayNG/app/src/main/res/layout/item_recycler_sub_setting.xml
+++ b/V2rayNG/app/src/main/res/layout/item_recycler_sub_setting.xml
@@ -6,115 +6,109 @@
android:layout_height="wrap_content"
android:gravity="center_vertical">
-
+ android:paddingTop="@dimen/layout_margin_spacing"
+ android:paddingBottom="@dimen/layout_margin_spacing">
+
+
+
+
+
+
+
+
+ android:orientation="vertical"
+ android:paddingStart="@dimen/padding_start"
+ android:paddingEnd="@dimen/padding_end">
+ android:gravity="center"
+ android:orientation="horizontal">
-
+ android:background="?attr/selectableItemBackgroundBorderless"
+ android:clickable="true"
+ android:focusable="true"
+ android:gravity="center"
+ android:orientation="vertical"
+ android:padding="@dimen/layout_margin_spacing">
-
+
+
+
+
+ android:background="?attr/selectableItemBackgroundBorderless"
+ android:clickable="true"
+ android:focusable="true"
+ android:gravity="center"
+ android:nextFocusLeft="@+id/info_container"
+ android:orientation="vertical"
+ android:padding="@dimen/layout_margin_spacing">
+
+
+
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/layout_margin_spacing"
+ android:orientation="horizontal">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ app:theme="@style/BrandedSwitch" />
+
-
diff --git a/V2rayNG/app/src/main/res/layout/item_recycler_user_asset.xml b/V2rayNG/app/src/main/res/layout/item_recycler_user_asset.xml
index cf1bf597..1fe9deb2 100644
--- a/V2rayNG/app/src/main/res/layout/item_recycler_user_asset.xml
+++ b/V2rayNG/app/src/main/res/layout/item_recycler_user_asset.xml
@@ -5,90 +5,84 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
-
+ android:layout_margin="@dimen/layout_margin_spacing"
+ android:background="?attr/selectableItemBackground"
+ android:gravity="center"
+ android:orientation="horizontal"
+ android:padding="@dimen/nav_header_vertical_spacing">
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:paddingStart="@dimen/padding_start">
+ android:orientation="vertical">
-
+ android:maxLines="2"
+ android:minLines="1"
+ android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
+ tools:text="Placeholder.dat" />
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/V2rayNG/app/src/main/res/values-night/colors.xml b/V2rayNG/app/src/main/res/values-night/colors.xml
index 35b55f82..ff1c835f 100644
--- a/V2rayNG/app/src/main/res/values-night/colors.xml
+++ b/V2rayNG/app/src/main/res/values-night/colors.xml
@@ -3,6 +3,7 @@
#f97910
#646464
#BDBDBD
+ #424242
#212121
#FFFFFF
diff --git a/V2rayNG/app/src/main/res/values/colors.xml b/V2rayNG/app/src/main/res/values/colors.xml
index 4d6f262c..8e328f84 100644
--- a/V2rayNG/app/src/main/res/values/colors.xml
+++ b/V2rayNG/app/src/main/res/values/colors.xml
@@ -7,6 +7,7 @@
#f97910
#9C9C9C
#727272
+ #E0E0E0
#F5F5F5
#000000
diff --git a/V2rayNG/app/src/main/res/values/dimens.xml b/V2rayNG/app/src/main/res/values/dimens.xml
index 84a8a180..313fa475 100644
--- a/V2rayNG/app/src/main/res/values/dimens.xml
+++ b/V2rayNG/app/src/main/res/values/dimens.xml
@@ -1,6 +1,6 @@
- 50dp
+ 64dp
16dp
16dp
8dp
From 3747e58e4eaeac95aeec72295f82afd109a63339 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Thu, 20 Mar 2025 10:24:38 +0800
Subject: [PATCH 067/238] Adjust dimen resources
---
.../com/v2ray/ang/ui/PerAppProxyAdapter.kt | 2 +-
.../src/main/res/layout/activity_about.xml | 86 +++++++++----------
.../main/res/layout/activity_bypass_list.xml | 6 +-
.../app/src/main/res/layout/activity_main.xml | 13 ++-
.../main/res/layout/activity_routing_edit.xml | 35 ++++----
.../res/layout/activity_routing_setting.xml | 10 +--
.../layout/activity_server_custom_config.xml | 12 +--
.../res/layout/activity_server_hysteria2.xml | 30 +++----
.../layout/activity_server_shadowsocks.xml | 14 +--
.../main/res/layout/activity_server_socks.xml | 14 +--
.../res/layout/activity_server_trojan.xml | 10 +--
.../main/res/layout/activity_server_vless.xml | 18 ++--
.../main/res/layout/activity_server_vmess.xml | 14 +--
.../res/layout/activity_server_wireguard.xml | 30 +++----
.../src/main/res/layout/activity_sub_edit.xml | 36 ++++----
.../src/main/res/layout/activity_tasker.xml | 4 +-
.../res/layout/activity_user_asset_url.xml | 12 +--
.../main/res/layout/dialog_config_filter.xml | 10 +--
.../app/src/main/res/layout/item_qrcode.xml | 4 +-
.../res/layout/item_recycler_bypass_list.xml | 12 +--
.../main/res/layout/item_recycler_footer.xml | 6 +-
.../main/res/layout/item_recycler_logcat.xml | 6 +-
.../main/res/layout/item_recycler_main.xml | 32 +++----
.../layout/item_recycler_routing_setting.xml | 34 ++++----
.../res/layout/item_recycler_sub_setting.xml | 32 +++----
.../res/layout/item_recycler_user_asset.xml | 18 ++--
.../main/res/layout/layout_address_port.xml | 12 +--
.../src/main/res/layout/layout_progress.xml | 2 +-
.../app/src/main/res/layout/layout_tls.xml | 32 +++----
.../main/res/layout/layout_tls_hysteria2.xml | 16 ++--
.../src/main/res/layout/layout_transport.xml | 20 ++---
.../app/src/main/res/layout/nav_header.xml | 6 +-
.../app/src/main/res/layout/widget_switch.xml | 2 +-
V2rayNG/app/src/main/res/values/dimens.xml | 27 +++---
34 files changed, 304 insertions(+), 313 deletions(-)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/PerAppProxyAdapter.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/PerAppProxyAdapter.kt
index deb585fe..0f150de3 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/PerAppProxyAdapter.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/PerAppProxyAdapter.kt
@@ -35,7 +35,7 @@ class PerAppProxyAdapter(val activity: BaseActivity, val apps: List, bl
val view = View(ctx)
view.layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
- ctx.resources.getDimensionPixelSize(R.dimen.bypass_list_header_height) * 0
+ ctx.resources.getDimensionPixelSize(R.dimen.view_height_dp64) * 0
)
BaseViewHolder(view)
}
diff --git a/V2rayNG/app/src/main/res/layout/activity_about.xml b/V2rayNG/app/src/main/res/layout/activity_about.xml
index 15262fd6..4651bafe 100644
--- a/V2rayNG/app/src/main/res/layout/activity_about.xml
+++ b/V2rayNG/app/src/main/res/layout/activity_about.xml
@@ -24,18 +24,18 @@
android:focusable="true"
android:gravity="center|start"
android:orientation="horizontal"
- android:padding="@dimen/padding">
+ android:padding="@dimen/padding_spacing_dp16">
+ android:paddingStart="@dimen/padding_spacing_dp16">
@@ -58,23 +58,23 @@
+ android:padding="@dimen/padding_spacing_dp16">
@@ -82,23 +82,23 @@
+ android:padding="@dimen/padding_spacing_dp16">
@@ -109,28 +109,28 @@
android:layout_height="match_parent"
android:gravity="top"
android:orientation="vertical"
- android:paddingTop="@dimen/padding_start">
+ android:paddingTop="@dimen/padding_spacing_dp16">
+ android:padding="@dimen/padding_spacing_dp16">
@@ -138,23 +138,23 @@
+ android:padding="@dimen/padding_spacing_dp16">
@@ -162,23 +162,23 @@
+ android:padding="@dimen/padding_spacing_dp16">
@@ -187,23 +187,23 @@
+ android:padding="@dimen/padding_spacing_dp16">
@@ -211,33 +211,33 @@
+ android:padding="@dimen/padding_spacing_dp16">
+ android:padding="@dimen/padding_spacing_dp16">
+ android:paddingStart="@dimen/padding_spacing_dp16"
+ android:paddingEnd="@dimen/padding_spacing_dp16">
@@ -63,7 +63,7 @@
@@ -89,7 +89,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
- android:layout_marginBottom="12dp">
+ android:layout_marginBottom="@dimen/margin_spacing_dp16">
+ android:padding="@dimen/margin_spacing_dp16">
@@ -143,7 +144,7 @@
@@ -162,7 +163,7 @@
@@ -182,8 +183,8 @@
diff --git a/V2rayNG/app/src/main/res/layout/activity_routing_setting.xml b/V2rayNG/app/src/main/res/layout/activity_routing_setting.xml
index 3032d6ce..17038020 100644
--- a/V2rayNG/app/src/main/res/layout/activity_routing_setting.xml
+++ b/V2rayNG/app/src/main/res/layout/activity_routing_setting.xml
@@ -20,7 +20,7 @@
@@ -39,10 +39,10 @@
+ android:paddingStart="@dimen/padding_spacing_dp16">
@@ -65,7 +65,7 @@
@@ -73,7 +73,7 @@
android:id="@+id/editor"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_marginTop="@dimen/layout_margin_top_height"
+ android:layout_marginTop="@dimen/margin_spacing_dp16"
android:gravity="top|start" />
diff --git a/V2rayNG/app/src/main/res/layout/activity_server_hysteria2.xml b/V2rayNG/app/src/main/res/layout/activity_server_hysteria2.xml
index a0228555..6ad4ffc0 100644
--- a/V2rayNG/app/src/main/res/layout/activity_server_hysteria2.xml
+++ b/V2rayNG/app/src/main/res/layout/activity_server_hysteria2.xml
@@ -10,14 +10,14 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
- android:padding="@dimen/layout_margin_top_height">
+ android:padding="@dimen/margin_spacing_dp16">
@@ -36,7 +36,7 @@
@@ -55,7 +55,7 @@
@@ -74,7 +74,7 @@
@@ -93,7 +93,7 @@
@@ -112,7 +112,7 @@
@@ -133,8 +133,8 @@
diff --git a/V2rayNG/app/src/main/res/layout/activity_server_shadowsocks.xml b/V2rayNG/app/src/main/res/layout/activity_server_shadowsocks.xml
index 83dbfc96..ed4466e8 100644
--- a/V2rayNG/app/src/main/res/layout/activity_server_shadowsocks.xml
+++ b/V2rayNG/app/src/main/res/layout/activity_server_shadowsocks.xml
@@ -10,14 +10,14 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
- android:padding="@dimen/layout_margin_top_height">
+ android:padding="@dimen/margin_spacing_dp16">
@@ -37,7 +37,7 @@
@@ -59,8 +59,8 @@
diff --git a/V2rayNG/app/src/main/res/layout/activity_server_socks.xml b/V2rayNG/app/src/main/res/layout/activity_server_socks.xml
index 42f62a3f..3f0c20e7 100644
--- a/V2rayNG/app/src/main/res/layout/activity_server_socks.xml
+++ b/V2rayNG/app/src/main/res/layout/activity_server_socks.xml
@@ -10,14 +10,14 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
- android:padding="@dimen/layout_margin_top_height">
+ android:padding="@dimen/margin_spacing_dp16">
@@ -37,7 +37,7 @@
@@ -56,8 +56,8 @@
diff --git a/V2rayNG/app/src/main/res/layout/activity_server_trojan.xml b/V2rayNG/app/src/main/res/layout/activity_server_trojan.xml
index b98753d0..8059d850 100644
--- a/V2rayNG/app/src/main/res/layout/activity_server_trojan.xml
+++ b/V2rayNG/app/src/main/res/layout/activity_server_trojan.xml
@@ -10,14 +10,14 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
- android:padding="@dimen/layout_margin_top_height">
+ android:padding="@dimen/margin_spacing_dp16">
@@ -40,8 +40,8 @@
diff --git a/V2rayNG/app/src/main/res/layout/activity_server_vless.xml b/V2rayNG/app/src/main/res/layout/activity_server_vless.xml
index c7bf624b..47bae4ce 100644
--- a/V2rayNG/app/src/main/res/layout/activity_server_vless.xml
+++ b/V2rayNG/app/src/main/res/layout/activity_server_vless.xml
@@ -10,14 +10,14 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
- android:padding="@dimen/layout_margin_top_height">
+ android:padding="@dimen/margin_spacing_dp16">
@@ -36,7 +36,7 @@
@@ -78,8 +78,8 @@
diff --git a/V2rayNG/app/src/main/res/layout/activity_server_vmess.xml b/V2rayNG/app/src/main/res/layout/activity_server_vmess.xml
index 413127e4..b541a67d 100644
--- a/V2rayNG/app/src/main/res/layout/activity_server_vmess.xml
+++ b/V2rayNG/app/src/main/res/layout/activity_server_vmess.xml
@@ -10,14 +10,14 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
- android:padding="@dimen/layout_margin_top_height">
+ android:padding="@dimen/margin_spacing_dp16">
@@ -36,7 +36,7 @@
@@ -58,8 +58,8 @@
diff --git a/V2rayNG/app/src/main/res/layout/activity_server_wireguard.xml b/V2rayNG/app/src/main/res/layout/activity_server_wireguard.xml
index 93bc43f7..498d15a7 100644
--- a/V2rayNG/app/src/main/res/layout/activity_server_wireguard.xml
+++ b/V2rayNG/app/src/main/res/layout/activity_server_wireguard.xml
@@ -10,14 +10,14 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
- android:padding="@dimen/layout_margin_top_height">
+ android:padding="@dimen/margin_spacing_dp16">
@@ -36,7 +36,7 @@
@@ -75,7 +75,7 @@
@@ -100,7 +100,7 @@
@@ -137,8 +137,8 @@
diff --git a/V2rayNG/app/src/main/res/layout/activity_sub_edit.xml b/V2rayNG/app/src/main/res/layout/activity_sub_edit.xml
index 504c1e07..aecaf02e 100644
--- a/V2rayNG/app/src/main/res/layout/activity_sub_edit.xml
+++ b/V2rayNG/app/src/main/res/layout/activity_sub_edit.xml
@@ -16,7 +16,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
- android:padding="@dimen/layout_margin_top_height">
+ android:padding="@dimen/margin_spacing_dp16">
@@ -52,7 +52,7 @@
@@ -96,7 +96,7 @@
@@ -118,7 +118,7 @@
@@ -141,7 +141,7 @@
@@ -161,7 +161,7 @@
@@ -182,8 +182,8 @@
diff --git a/V2rayNG/app/src/main/res/layout/activity_tasker.xml b/V2rayNG/app/src/main/res/layout/activity_tasker.xml
index 6fb211b1..c97811bf 100644
--- a/V2rayNG/app/src/main/res/layout/activity_tasker.xml
+++ b/V2rayNG/app/src/main/res/layout/activity_tasker.xml
@@ -5,12 +5,12 @@
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical"
- android:padding="@dimen/padding"
+ android:padding="@dimen/padding_spacing_dp16"
tools:context=".ui.TaskerActivity">
+ android:layout_height="@dimen/view_height_dp64">
+ android:padding="@dimen/margin_spacing_dp16">
@@ -51,7 +51,7 @@
diff --git a/V2rayNG/app/src/main/res/layout/dialog_config_filter.xml b/V2rayNG/app/src/main/res/layout/dialog_config_filter.xml
index 1c043f4f..4b9b97ac 100644
--- a/V2rayNG/app/src/main/res/layout/dialog_config_filter.xml
+++ b/V2rayNG/app/src/main/res/layout/dialog_config_filter.xml
@@ -7,25 +7,25 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
- android:padding="@dimen/layout_margin_top_height">
+ android:padding="@dimen/margin_spacing_dp16">
+ android:layout_height="@dimen/view_height_dp48" />
diff --git a/V2rayNG/app/src/main/res/layout/item_qrcode.xml b/V2rayNG/app/src/main/res/layout/item_qrcode.xml
index d5997b3f..d3ea9075 100644
--- a/V2rayNG/app/src/main/res/layout/item_qrcode.xml
+++ b/V2rayNG/app/src/main/res/layout/item_qrcode.xml
@@ -9,8 +9,8 @@
diff --git a/V2rayNG/app/src/main/res/layout/item_recycler_bypass_list.xml b/V2rayNG/app/src/main/res/layout/item_recycler_bypass_list.xml
index fee1de30..b2d62b6f 100644
--- a/V2rayNG/app/src/main/res/layout/item_recycler_bypass_list.xml
+++ b/V2rayNG/app/src/main/res/layout/item_recycler_bypass_list.xml
@@ -9,13 +9,13 @@
+ android:layout_width="@dimen/view_height_dp72"
+ android:layout_height="@dimen/view_height_dp72"
+ android:padding="@dimen/padding_spacing_dp16" />
@@ -32,7 +32,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="3"
- android:paddingTop="@dimen/layout_margin_spacing"
+ android:paddingTop="@dimen/margin_spacing_dp8"
android:textAppearance="@style/TextAppearance.AppCompat.Small" />
@@ -42,6 +42,6 @@
android:layout_height="wrap_content"
android:clickable="false"
android:focusable="false"
- android:padding="@dimen/padding"/>
+ android:padding="@dimen/padding_spacing_dp16"/>
\ No newline at end of file
diff --git a/V2rayNG/app/src/main/res/layout/item_recycler_footer.xml b/V2rayNG/app/src/main/res/layout/item_recycler_footer.xml
index 6c4df973..e02cdc43 100644
--- a/V2rayNG/app/src/main/res/layout/item_recycler_footer.xml
+++ b/V2rayNG/app/src/main/res/layout/item_recycler_footer.xml
@@ -8,18 +8,18 @@
+ android:padding="@dimen/padding_spacing_dp16">
+ android:paddingBottom="@dimen/margin_spacing_dp16">
+ android:layout_margin="@dimen/margin_spacing_dp8"
+ android:paddingTop="@dimen/margin_spacing_dp8"
+ android:paddingBottom="@dimen/margin_spacing_dp8">
+ android:paddingStart="@dimen/padding_spacing_dp16">
+ android:paddingEnd="@dimen/padding_spacing_dp16">
+ android:padding="@dimen/margin_spacing_dp8">
@@ -128,17 +128,17 @@
+ android:padding="@dimen/margin_spacing_dp8">
@@ -146,17 +146,17 @@
+ android:padding="@dimen/margin_spacing_dp8">
@@ -168,7 +168,7 @@
android:layout_gravity="right"
android:orientation="vertical"
android:paddingBottom="4dp"
- android:paddingEnd="@dimen/padding_end">
+ android:paddingEnd="@dimen/padding_spacing_dp16">
+ android:paddingTop="@dimen/margin_spacing_dp8"
+ android:paddingBottom="@dimen/margin_spacing_dp8">
+ android:paddingStart="@dimen/padding_spacing_dp16">
@@ -53,7 +53,7 @@
android:id="@+id/domainIp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/layout_margin_spacing"
+ android:layout_marginTop="@dimen/margin_spacing_dp8"
android:lines="1"
android:textAppearance="@style/TextAppearance.AppCompat.Small" />
@@ -61,7 +61,7 @@
android:id="@+id/outboundTag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/layout_margin_spacing"
+ android:layout_marginTop="@dimen/margin_spacing_dp8"
android:lines="1"
android:textAppearance="@style/TextAppearance.AppCompat.Small" />
@@ -69,11 +69,11 @@
+ android:paddingStart="@dimen/padding_spacing_dp16"
+ android:paddingEnd="@dimen/padding_spacing_dp16">
+ android:padding="@dimen/margin_spacing_dp8">
+ android:paddingTop="@dimen/margin_spacing_dp8"
+ android:paddingBottom="@dimen/margin_spacing_dp8">
+ android:paddingStart="@dimen/padding_spacing_dp16">
@@ -46,11 +46,11 @@
+ android:paddingStart="@dimen/padding_spacing_dp16"
+ android:paddingEnd="@dimen/padding_spacing_dp16">
+ android:padding="@dimen/margin_spacing_dp8">
@@ -86,11 +86,11 @@
android:gravity="center"
android:nextFocusLeft="@+id/info_container"
android:orientation="vertical"
- android:padding="@dimen/layout_margin_spacing">
+ android:padding="@dimen/margin_spacing_dp8">
@@ -99,7 +99,7 @@
+ android:padding="@dimen/margin_spacing_dp8">
+ android:paddingStart="@dimen/padding_spacing_dp16">
+ android:padding="@dimen/margin_spacing_dp8">
@@ -76,11 +76,11 @@
android:background="?attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:focusable="true"
- android:padding="@dimen/nav_header_vertical_spacing">
+ android:padding="@dimen/margin_spacing_dp8">
diff --git a/V2rayNG/app/src/main/res/layout/layout_address_port.xml b/V2rayNG/app/src/main/res/layout/layout_address_port.xml
index 66c46609..b34c8b5f 100644
--- a/V2rayNG/app/src/main/res/layout/layout_address_port.xml
+++ b/V2rayNG/app/src/main/res/layout/layout_address_port.xml
@@ -19,7 +19,7 @@
@@ -38,7 +38,7 @@
@@ -57,7 +57,7 @@
diff --git a/V2rayNG/app/src/main/res/layout/layout_progress.xml b/V2rayNG/app/src/main/res/layout/layout_progress.xml
index 8e431cad..2f994ab0 100644
--- a/V2rayNG/app/src/main/res/layout/layout_progress.xml
+++ b/V2rayNG/app/src/main/res/layout/layout_progress.xml
@@ -5,7 +5,7 @@
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="horizontal"
- android:padding="@dimen/padding">
+ android:padding="@dimen/padding_spacing_dp16">
@@ -21,7 +21,7 @@
@@ -31,7 +31,7 @@
android:id="@+id/lay_sni"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/activity_horizontal_margin"
+ android:layout_marginBottom="@dimen/margin_spacing_dp16"
android:orientation="vertical">
@@ -53,7 +53,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
- android:layout_marginBottom="@dimen/activity_horizontal_margin">
+ android:layout_marginBottom="@dimen/margin_spacing_dp16">
@@ -74,7 +74,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
- android:layout_marginBottom="@dimen/activity_horizontal_margin">
+ android:layout_marginBottom="@dimen/margin_spacing_dp16">
@@ -104,7 +104,7 @@
@@ -112,7 +112,7 @@
android:id="@+id/lay_public_key"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/activity_horizontal_margin"
+ android:layout_marginBottom="@dimen/margin_spacing_dp16"
android:orientation="vertical">
@@ -133,7 +133,7 @@
android:id="@+id/lay_short_id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/activity_horizontal_margin"
+ android:layout_marginBottom="@dimen/margin_spacing_dp16"
android:orientation="vertical">
@@ -154,7 +154,7 @@
android:id="@+id/lay_spider_x"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/activity_horizontal_margin"
+ android:layout_marginBottom="@dimen/margin_spacing_dp16"
android:orientation="vertical">
diff --git a/V2rayNG/app/src/main/res/layout/layout_tls_hysteria2.xml b/V2rayNG/app/src/main/res/layout/layout_tls_hysteria2.xml
index 3180f666..6814af4e 100644
--- a/V2rayNG/app/src/main/res/layout/layout_tls_hysteria2.xml
+++ b/V2rayNG/app/src/main/res/layout/layout_tls_hysteria2.xml
@@ -2,14 +2,14 @@
@@ -21,7 +21,7 @@
@@ -31,7 +31,7 @@
android:id="@+id/lay_sni"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/activity_horizontal_margin"
+ android:layout_marginBottom="@dimen/margin_spacing_dp16"
android:orientation="vertical">
@@ -63,14 +63,14 @@
diff --git a/V2rayNG/app/src/main/res/layout/layout_transport.xml b/V2rayNG/app/src/main/res/layout/layout_transport.xml
index bda1e11c..12136d59 100644
--- a/V2rayNG/app/src/main/res/layout/layout_transport.xml
+++ b/V2rayNG/app/src/main/res/layout/layout_transport.xml
@@ -7,7 +7,7 @@
+ android:layout_height="@dimen/view_height_dp48" />
@@ -76,7 +76,7 @@
@@ -96,7 +96,7 @@
android:id="@+id/layout_extra"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/layout_margin_top_height"
+ android:layout_marginTop="@dimen/margin_spacing_dp16"
android:orientation="vertical">
+ android:padding="@dimen/margin_spacing_dp16">
diff --git a/V2rayNG/app/src/main/res/layout/widget_switch.xml b/V2rayNG/app/src/main/res/layout/widget_switch.xml
index bf0bfc71..50776f87 100644
--- a/V2rayNG/app/src/main/res/layout/widget_switch.xml
+++ b/V2rayNG/app/src/main/res/layout/widget_switch.xml
@@ -18,7 +18,7 @@
android:id="@+id/image_switch"
android:layout_width="45dp"
android:layout_height="45dp"
- android:padding="@dimen/padding"
+ android:padding="@dimen/padding_spacing_dp16"
app:srcCompat="@drawable/ic_stat_name" />
diff --git a/V2rayNG/app/src/main/res/values/dimens.xml b/V2rayNG/app/src/main/res/values/dimens.xml
index 313fa475..4a4a33b4 100644
--- a/V2rayNG/app/src/main/res/values/dimens.xml
+++ b/V2rayNG/app/src/main/res/values/dimens.xml
@@ -1,21 +1,14 @@
- 64dp
- 16dp
- 16dp
- 8dp
- 50dp
- 24dp
- 72dp
- 90dp
- 60dp
- 16dp
- 16dp
- 16dp
- 3dp
+ 8dp
+ 16dp
+ 16dp
- 16dp
- 16dp
- 8dp
- 160dp
+ 24dp
+
+ 48dp
+ 64dp
+ 72dp
+ 90dp
+ 160dp
From 0b9a96209f878a03ee7f54e3291f1ba614ded15f Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Thu, 20 Mar 2025 12:00:33 +0800
Subject: [PATCH 068/238] Improved progress bar
---
.../com/v2ray/ang/ui/PerAppProxyActivity.kt | 6 ++--
.../com/v2ray/ang/ui/SubSettingActivity.kt | 10 ++----
.../com/v2ray/ang/ui/UserAssetActivity.kt | 35 +++++++++----------
.../main/res/layout/activity_bypass_list.xml | 8 +++--
.../main/res/layout/activity_sub_setting.xml | 9 +++++
.../src/main/res/layout/layout_progress.xml | 16 ---------
6 files changed, 37 insertions(+), 47 deletions(-)
delete mode 100644 V2rayNG/app/src/main/res/layout/layout_progress.xml
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/PerAppProxyActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/PerAppProxyActivity.kt
index 0be214b8..e1b6f3b3 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/PerAppProxyActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/PerAppProxyActivity.kt
@@ -44,7 +44,7 @@ class PerAppProxyActivity : BaseActivity() {
lifecycleScope.launch {
try {
- binding.pbWaiting.visibility = View.VISIBLE
+ binding.pbWaiting.show()
val blacklist = MmkvManager.decodeSettingsStringSet(AppConfig.PREF_PER_APP_PROXY_SET)
val apps = withContext(Dispatchers.IO) {
val appsList = AppManagerUtil.loadNetworkAppList(this@PerAppProxyActivity)
@@ -69,9 +69,9 @@ class PerAppProxyActivity : BaseActivity() {
appsAll = apps
adapter = PerAppProxyAdapter(this@PerAppProxyActivity, apps, blacklist)
binding.recyclerView.adapter = adapter
- binding.pbWaiting.visibility = View.GONE
+ binding.pbWaiting.hide()
} catch (e: Exception) {
- binding.pbWaiting.visibility = View.GONE
+ binding.pbWaiting.hide()
Log.e(ANG_PACKAGE, "Error loading apps", e)
}
}
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/SubSettingActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/SubSettingActivity.kt
index 3fe8d85a..f2c304e2 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/SubSettingActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/SubSettingActivity.kt
@@ -4,13 +4,12 @@ import android.content.Intent
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
-import androidx.appcompat.app.AlertDialog
+import android.view.View
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import com.v2ray.ang.R
import com.v2ray.ang.databinding.ActivitySubSettingBinding
-import com.v2ray.ang.databinding.LayoutProgressBinding
import com.v2ray.ang.dto.SubscriptionItem
import com.v2ray.ang.extension.toast
import com.v2ray.ang.handler.AngConfigManager
@@ -59,10 +58,7 @@ class SubSettingActivity : BaseActivity() {
}
R.id.sub_update -> {
- val dialog = AlertDialog.Builder(this)
- .setView(LayoutProgressBinding.inflate(layoutInflater).root)
- .setCancelable(false)
- .show()
+ binding.pbWaiting.show()
lifecycleScope.launch(Dispatchers.IO) {
val count = AngConfigManager.updateConfigViaSubAll()
@@ -73,7 +69,7 @@ class SubSettingActivity : BaseActivity() {
} else {
toast(R.string.toast_failure)
}
- dialog.dismiss()
+ binding.pbWaiting.hide()
}
}
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/UserAssetActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/UserAssetActivity.kt
index 8afcb8fa..e2335c16 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/UserAssetActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/UserAssetActivity.kt
@@ -12,6 +12,7 @@ import android.util.Log
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuItem
+import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.ViewGroup
@@ -23,7 +24,6 @@ import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
import com.v2ray.ang.databinding.ActivitySubSettingBinding
import com.v2ray.ang.databinding.ItemRecyclerUserAssetBinding
-import com.v2ray.ang.databinding.LayoutProgressBinding
import com.v2ray.ang.dto.AssetUrlItem
import com.v2ray.ang.extension.toTrafficString
import com.v2ray.ang.extension.toast
@@ -196,32 +196,31 @@ class UserAssetActivity : BaseActivity() {
}
private fun downloadGeoFiles() {
- val dialog = AlertDialog.Builder(this)
- .setView(LayoutProgressBinding.inflate(layoutInflater).root)
- .setCancelable(false)
- .show()
+ binding.pbWaiting.show()
toast(R.string.msg_downloading_content)
val httpPort = SettingsManager.getHttpPort()
var assets = MmkvManager.decodeAssetUrls()
assets = addBuiltInGeoItems(assets)
- assets.forEach {
- //toast(getString(R.string.msg_downloading_content) + it)
- lifecycleScope.launch(Dispatchers.IO) {
- var result = downloadGeo(it.second, 60000, httpPort)
+ var resultCount = 0
+ lifecycleScope.launch(Dispatchers.IO) {
+ assets.forEach {
+ var result = downloadGeo(it.second, 30000, httpPort)
if (!result) {
- result = downloadGeo(it.second, 60000, 0)
+ result = downloadGeo(it.second, 30000, 0)
}
- launch(Dispatchers.Main) {
- if (result) {
- toast(getString(R.string.toast_success) + " " + it.second.remarks)
- binding.recyclerView.adapter?.notifyDataSetChanged()
- } else {
- toast(getString(R.string.toast_failure) + " " + it.second.remarks)
- }
- dialog.dismiss()
+ if (result)
+ resultCount++
+ }
+ withContext(Dispatchers.Main) {
+ if (resultCount > 0) {
+ toast(getString(R.string.title_update_config_count, resultCount))
+ binding.recyclerView.adapter?.notifyDataSetChanged()
+ } else {
+ toast(getString(R.string.toast_failure))
}
+ binding.pbWaiting.hide()
}
}
}
diff --git a/V2rayNG/app/src/main/res/layout/activity_bypass_list.xml b/V2rayNG/app/src/main/res/layout/activity_bypass_list.xml
index 73831704..a0b9d459 100644
--- a/V2rayNG/app/src/main/res/layout/activity_bypass_list.xml
+++ b/V2rayNG/app/src/main/res/layout/activity_bypass_list.xml
@@ -71,11 +71,13 @@
-
+ android:layout_height="wrap_content"
+ android:indeterminate="true"
+ android:visibility="invisible"
+ app:indicatorColor="@color/color_fab_active" />
@@ -12,6 +13,14 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
+
+
-
-
-
-
-
-
\ No newline at end of file
From a60f45ce31b5486e817b6615b8db1efb4fa34b31 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Thu, 20 Mar 2025 12:06:13 +0800
Subject: [PATCH 069/238] The timeout was changed from 30s to 15s
---
.../java/com/v2ray/ang/handler/AngConfigManager.kt | 2 +-
.../java/com/v2ray/ang/handler/SpeedtestManager.kt | 2 +-
.../main/java/com/v2ray/ang/ui/UserAssetActivity.kt | 4 ++--
.../app/src/main/java/com/v2ray/ang/util/HttpUtil.kt | 10 +++++-----
4 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/AngConfigManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/AngConfigManager.kt
index 10e28b4c..27061810 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/AngConfigManager.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/AngConfigManager.kt
@@ -420,7 +420,7 @@ object AngConfigManager {
var configText = try {
val httpPort = SettingsManager.getHttpPort()
- HttpUtil.getUrlContentWithUserAgent(url, 30000, httpPort)
+ HttpUtil.getUrlContentWithUserAgent(url, 15000, httpPort)
} catch (e: Exception) {
Log.e(AppConfig.ANG_PACKAGE, "Update subscription: proxy not ready or other error, try……")
//e.printStackTrace()
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SpeedtestManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SpeedtestManager.kt
index 6ac62c20..d607f0e5 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SpeedtestManager.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SpeedtestManager.kt
@@ -135,7 +135,7 @@ object SpeedtestManager {
var result: String
var elapsed = -1L
- val conn = HttpUtil.createProxyConnection(SettingsManager.getDelayTestUrl(), port, 30000, 30000) ?: return Pair(elapsed, "")
+ val conn = HttpUtil.createProxyConnection(SettingsManager.getDelayTestUrl(), port, 15000, 15000) ?: return Pair(elapsed, "")
try {
val start = SystemClock.elapsedRealtime()
val code = conn.responseCode
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/UserAssetActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/UserAssetActivity.kt
index e2335c16..cf196a96 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/UserAssetActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/UserAssetActivity.kt
@@ -206,9 +206,9 @@ class UserAssetActivity : BaseActivity() {
var resultCount = 0
lifecycleScope.launch(Dispatchers.IO) {
assets.forEach {
- var result = downloadGeo(it.second, 30000, httpPort)
+ var result = downloadGeo(it.second, 15000, httpPort)
if (!result) {
- result = downloadGeo(it.second, 30000, 0)
+ result = downloadGeo(it.second, 15000, 0)
}
if (result)
resultCount++
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt
index dc0ab7da..2f5e506c 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt
@@ -50,7 +50,7 @@ object HttpUtil {
* @throws IOException If an I/O error occurs.
*/
@Throws(IOException::class)
- fun getUrlContentWithUserAgent(url: String?, timeout: Int = 30000, httpPort: Int = 0): String {
+ fun getUrlContentWithUserAgent(url: String?, timeout: Int = 15000, httpPort: Int = 0): String {
var currentUrl = url
var redirects = 0
val maxRedirects = 3
@@ -88,16 +88,16 @@ object HttpUtil {
*
* @param urlStr The target URL address.
* @param port The port of the proxy server.
- * @param connectTimeout The connection timeout in milliseconds (default is 30000 ms).
- * @param readTimeout The read timeout in milliseconds (default is 30000 ms).
+ * @param connectTimeout The connection timeout in milliseconds (default is 15000 ms).
+ * @param readTimeout The read timeout in milliseconds (default is 15000 ms).
* @param needStream Whether the connection needs to support streaming.
* @return Returns a configured HttpURLConnection object, or null if it fails.
*/
fun createProxyConnection(
urlStr: String,
port: Int,
- connectTimeout: Int = 30000,
- readTimeout: Int = 30000,
+ connectTimeout: Int = 15000,
+ readTimeout: Int = 15000,
needStream: Boolean = false
): HttpURLConnection? {
From 14ff9eb5274f0b3a0bc91a950ed0fb2b6bc456dd Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Thu, 20 Mar 2025 14:37:38 +0800
Subject: [PATCH 070/238] Optimize and improve PerAppProxyActivity
---
.../java/com/v2ray/ang/ui/PerAppProxyActivity.kt | 15 ++++++++++-----
.../src/main/java/com/v2ray/ang/util/HttpUtil.kt | 10 +++++-----
.../src/main/res/layout/activity_bypass_list.xml | 16 ++++++++--------
3 files changed, 23 insertions(+), 18 deletions(-)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/PerAppProxyActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/PerAppProxyActivity.kt
index e1b6f3b3..e286034b 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/PerAppProxyActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/PerAppProxyActivity.kt
@@ -5,11 +5,8 @@ import android.text.TextUtils
import android.util.Log
import android.view.Menu
import android.view.MenuItem
-import android.view.View
import androidx.appcompat.widget.SearchView
import androidx.lifecycle.lifecycleScope
-import androidx.recyclerview.widget.DividerItemDecoration
-import androidx.recyclerview.widget.LinearLayoutManager
import com.v2ray.ang.AppConfig
import com.v2ray.ang.AppConfig.ANG_PACKAGE
import com.v2ray.ang.R
@@ -18,6 +15,7 @@ import com.v2ray.ang.dto.AppInfo
import com.v2ray.ang.extension.toast
import com.v2ray.ang.extension.v2RayApplication
import com.v2ray.ang.handler.MmkvManager
+import com.v2ray.ang.handler.SettingsManager
import com.v2ray.ang.util.AppManagerUtil
import com.v2ray.ang.util.HttpUtil
import com.v2ray.ang.util.Utils
@@ -48,7 +46,7 @@ class PerAppProxyActivity : BaseActivity() {
val blacklist = MmkvManager.decodeSettingsStringSet(AppConfig.PREF_PER_APP_PROXY_SET)
val apps = withContext(Dispatchers.IO) {
val appsList = AppManagerUtil.loadNetworkAppList(this@PerAppProxyActivity)
-
+
if (blacklist != null) {
appsList.forEach { app ->
app.isSelected = if (blacklist.contains(app.packageName)) 1 else 0
@@ -152,13 +150,20 @@ class PerAppProxyActivity : BaseActivity() {
private fun selectProxyApp() {
toast(R.string.msg_downloading_content)
+ binding.pbWaiting.show()
+
val url = AppConfig.androidpackagenamelistUrl
lifecycleScope.launch(Dispatchers.IO) {
- val content = HttpUtil.getUrlContent(url, 5000)
+ var content = HttpUtil.getUrlContent(url, 5000)
+ if (content.isNullOrEmpty()) {
+ val httpPort = SettingsManager.getHttpPort()
+ content = HttpUtil.getUrlContent(url, 5000, httpPort) ?: ""
+ }
launch(Dispatchers.Main) {
Log.d(ANG_PACKAGE, content)
selectProxyApp(content, true)
toast(R.string.toast_success)
+ binding.pbWaiting.hide()
}
}
}
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt
index 2f5e506c..135353ad 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt
@@ -26,18 +26,18 @@ object HttpUtil {
*
* @param url The URL to fetch content from.
* @param timeout The timeout value in milliseconds.
+ * @param httpPort The HTTP port to use.
* @return The content of the URL as a string.
*/
- fun getUrlContent(url: String, timeout: Int): String {
- var result: String = ""
- val conn = createProxyConnection(url, 0, timeout, timeout) ?: return result
+ fun getUrlContent(url: String, timeout: Int, httpPort: Int = 0): String? {
+ val conn = createProxyConnection(url, httpPort, timeout, timeout) ?: return null
try {
- result = conn.inputStream.bufferedReader().readText()
+ return conn.inputStream.bufferedReader().readText()
} catch (_: Exception) {
} finally {
conn.disconnect()
}
- return result
+ return null
}
/**
diff --git a/V2rayNG/app/src/main/res/layout/activity_bypass_list.xml b/V2rayNG/app/src/main/res/layout/activity_bypass_list.xml
index a0b9d459..e40edd72 100644
--- a/V2rayNG/app/src/main/res/layout/activity_bypass_list.xml
+++ b/V2rayNG/app/src/main/res/layout/activity_bypass_list.xml
@@ -7,6 +7,14 @@
android:fitsSystemWindows="true"
android:orientation="vertical">
+
+
-
-
Date: Thu, 20 Mar 2025 14:37:53 +0800
Subject: [PATCH 071/238] Optimize and improve
---
.../com/v2ray/ang/handler/AngConfigManager.kt | 183 +++++++++---------
.../v2ray/ang/handler/V2rayConfigManager.kt | 7 +-
.../com/v2ray/ang/service/V2RayTestService.kt | 2 +-
.../com/v2ray/ang/ui/MainRecyclerAdapter.kt | 2 +-
.../com/v2ray/ang/ui/SubSettingActivity.kt | 1 -
.../com/v2ray/ang/ui/UserAssetActivity.kt | 1 -
.../com/v2ray/ang/viewmodel/MainViewModel.kt | 2 +-
7 files changed, 98 insertions(+), 100 deletions(-)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/AngConfigManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/AngConfigManager.kt
index 27061810..13bba449 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/AngConfigManager.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/AngConfigManager.kt
@@ -24,94 +24,6 @@ import java.net.URI
object AngConfigManager {
- /**
- * Parses the configuration from a QR code or string.
- *
- * @param str The configuration string.
- * @param subid The subscription ID.
- * @param subItem The subscription item.
- * @param removedSelectedServer The removed selected server.
- * @return The result code.
- */
- private fun parseConfig(
- str: String?,
- subid: String,
- subItem: SubscriptionItem?,
- removedSelectedServer: ProfileItem?
- ): Int {
- try {
- if (str == null || TextUtils.isEmpty(str)) {
- return R.string.toast_none_data
- }
-
- val config = if (str.startsWith(EConfigType.VMESS.protocolScheme)) {
- VmessFmt.parse(str)
- } else if (str.startsWith(EConfigType.SHADOWSOCKS.protocolScheme)) {
- ShadowsocksFmt.parse(str)
- } else if (str.startsWith(EConfigType.SOCKS.protocolScheme)) {
- SocksFmt.parse(str)
- } else if (str.startsWith(EConfigType.TROJAN.protocolScheme)) {
- TrojanFmt.parse(str)
- } else if (str.startsWith(EConfigType.VLESS.protocolScheme)) {
- VlessFmt.parse(str)
- } else if (str.startsWith(EConfigType.WIREGUARD.protocolScheme)) {
- WireguardFmt.parse(str)
- } else if (str.startsWith(EConfigType.HYSTERIA2.protocolScheme) || str.startsWith(HY2)) {
- Hysteria2Fmt.parse(str)
- } else {
- null
- }
-
- if (config == null) {
- return R.string.toast_incorrect_protocol
- }
- //filter
- if (subItem?.filter != null && subItem.filter?.isNotEmpty() == true && config.remarks.isNotEmpty()) {
- val matched = Regex(pattern = subItem.filter ?: "")
- .containsMatchIn(input = config.remarks)
- if (!matched) return -1
- }
-
- config.subscriptionId = subid
- val guid = MmkvManager.encodeServerConfig("", config)
- if (removedSelectedServer != null &&
- config.server == removedSelectedServer.server && config.serverPort == removedSelectedServer.serverPort
- ) {
- MmkvManager.setSelectServer(guid)
- }
- } catch (e: Exception) {
- e.printStackTrace()
- return -1
- }
- return 0
- }
-
- /**
- * Shares the configuration.
- *
- * @param guid The GUID of the configuration.
- * @return The configuration string.
- */
- private fun shareConfig(guid: String): String {
- try {
- val config = MmkvManager.decodeServerConfig(guid) ?: return ""
-
- return config.configType.protocolScheme + when (config.configType) {
- EConfigType.VMESS -> VmessFmt.toUri(config)
- EConfigType.CUSTOM -> ""
- EConfigType.SHADOWSOCKS -> ShadowsocksFmt.toUri(config)
- EConfigType.SOCKS -> SocksFmt.toUri(config)
- EConfigType.HTTP -> ""
- EConfigType.VLESS -> VlessFmt.toUri(config)
- EConfigType.TROJAN -> TrojanFmt.toUri(config)
- EConfigType.WIREGUARD -> WireguardFmt.toUri(config)
- EConfigType.HYSTERIA2 -> Hysteria2Fmt.toUri(config)
- }
- } catch (e: Exception) {
- e.printStackTrace()
- return ""
- }
- }
/**
* Shares the configuration to the clipboard.
@@ -214,6 +126,33 @@ object AngConfigManager {
return 0
}
+ /**
+ * Shares the configuration.
+ *
+ * @param guid The GUID of the configuration.
+ * @return The configuration string.
+ */
+ private fun shareConfig(guid: String): String {
+ try {
+ val config = MmkvManager.decodeServerConfig(guid) ?: return ""
+
+ return config.configType.protocolScheme + when (config.configType) {
+ EConfigType.VMESS -> VmessFmt.toUri(config)
+ EConfigType.CUSTOM -> ""
+ EConfigType.SHADOWSOCKS -> ShadowsocksFmt.toUri(config)
+ EConfigType.SOCKS -> SocksFmt.toUri(config)
+ EConfigType.HTTP -> ""
+ EConfigType.VLESS -> VlessFmt.toUri(config)
+ EConfigType.TROJAN -> TrojanFmt.toUri(config)
+ EConfigType.WIREGUARD -> WireguardFmt.toUri(config)
+ EConfigType.HYSTERIA2 -> Hysteria2Fmt.toUri(config)
+ }
+ } catch (e: Exception) {
+ e.printStackTrace()
+ return ""
+ }
+ }
+
/**
* Imports a batch of configurations.
*
@@ -248,7 +187,7 @@ object AngConfigManager {
* @param servers The servers string.
* @return The number of subscriptions parsed.
*/
- fun parseBatchSubscription(servers: String?): Int {
+ private fun parseBatchSubscription(servers: String?): Int {
try {
if (servers == null) {
return 0
@@ -277,7 +216,7 @@ object AngConfigManager {
* @param append Whether to append the configurations.
* @return The number of configurations parsed.
*/
- fun parseBatchConfig(servers: String?, subid: String, append: Boolean): Int {
+ private fun parseBatchConfig(servers: String?, subid: String, append: Boolean): Int {
try {
if (servers == null) {
return 0
@@ -324,7 +263,7 @@ object AngConfigManager {
* @param subid The subscription ID.
* @return The number of configurations parsed.
*/
- fun parseCustomConfigServer(server: String?, subid: String): Int {
+ private fun parseCustomConfigServer(server: String?, subid: String): Int {
if (server == null) {
return 0
}
@@ -377,6 +316,68 @@ object AngConfigManager {
}
}
+ /**
+ * Parses the configuration from a QR code or string.
+ *
+ * @param str The configuration string.
+ * @param subid The subscription ID.
+ * @param subItem The subscription item.
+ * @param removedSelectedServer The removed selected server.
+ * @return The result code.
+ */
+ private fun parseConfig(
+ str: String?,
+ subid: String,
+ subItem: SubscriptionItem?,
+ removedSelectedServer: ProfileItem?
+ ): Int {
+ try {
+ if (str == null || TextUtils.isEmpty(str)) {
+ return R.string.toast_none_data
+ }
+
+ val config = if (str.startsWith(EConfigType.VMESS.protocolScheme)) {
+ VmessFmt.parse(str)
+ } else if (str.startsWith(EConfigType.SHADOWSOCKS.protocolScheme)) {
+ ShadowsocksFmt.parse(str)
+ } else if (str.startsWith(EConfigType.SOCKS.protocolScheme)) {
+ SocksFmt.parse(str)
+ } else if (str.startsWith(EConfigType.TROJAN.protocolScheme)) {
+ TrojanFmt.parse(str)
+ } else if (str.startsWith(EConfigType.VLESS.protocolScheme)) {
+ VlessFmt.parse(str)
+ } else if (str.startsWith(EConfigType.WIREGUARD.protocolScheme)) {
+ WireguardFmt.parse(str)
+ } else if (str.startsWith(EConfigType.HYSTERIA2.protocolScheme) || str.startsWith(HY2)) {
+ Hysteria2Fmt.parse(str)
+ } else {
+ null
+ }
+
+ if (config == null) {
+ return R.string.toast_incorrect_protocol
+ }
+ //filter
+ if (subItem?.filter != null && subItem.filter?.isNotEmpty() == true && config.remarks.isNotEmpty()) {
+ val matched = Regex(pattern = subItem.filter ?: "")
+ .containsMatchIn(input = config.remarks)
+ if (!matched) return -1
+ }
+
+ config.subscriptionId = subid
+ val guid = MmkvManager.encodeServerConfig("", config)
+ if (removedSelectedServer != null &&
+ config.server == removedSelectedServer.server && config.serverPort == removedSelectedServer.serverPort
+ ) {
+ MmkvManager.setSelectServer(guid)
+ }
+ } catch (e: Exception) {
+ e.printStackTrace()
+ return -1
+ }
+ return 0
+ }
+
/**
* Updates the configuration via all subscriptions.
*
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt
index e0af5755..edbf8811 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt
@@ -97,7 +97,6 @@ object V2rayConfigManager {
}
}
- //取得默认配置
val assets = Utils.readTextFromAssets(context, "v2ray_config.json")
if (TextUtils.isEmpty(assets)) {
return result
@@ -284,7 +283,7 @@ object V2rayConfigManager {
)
}
- // DNS inbound对象
+ // DNS inbound
val remoteDns = SettingsManager.getRemoteDnsServers()
if (v2rayConfig.inbounds.none { e -> e.protocol == "dokodemo-door" && e.tag == "dns-in" }) {
val dnsInboundSettings = V2rayConfig.InboundBean.InSettingsBean(
@@ -309,7 +308,7 @@ object V2rayConfigManager {
)
}
- // DNS outbound对象
+ // DNS outbound
if (v2rayConfig.outbounds.none { e -> e.protocol == "dns" && e.tag == "dns-out" }) {
v2rayConfig.outbounds.add(
V2rayConfig.OutboundBean(
@@ -416,7 +415,7 @@ object V2rayConfigManager {
hosts[DNS_YANDEX_DOMAIN] = DNS_YANDEX_ADDRESSES
- // DNS dns对象
+ // DNS dns
v2rayConfig.dns = V2rayConfig.DnsBean(
servers = servers,
hosts = hosts
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayTestService.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayTestService.kt
index 8bdbc6d1..193489ba 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayTestService.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayTestService.kt
@@ -9,10 +9,10 @@ import com.v2ray.ang.AppConfig.MSG_MEASURE_CONFIG_SUCCESS
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.extension.serializable
import com.v2ray.ang.handler.MmkvManager
+import com.v2ray.ang.handler.SpeedtestManager
import com.v2ray.ang.handler.V2rayConfigManager
import com.v2ray.ang.util.MessageUtil
import com.v2ray.ang.util.PluginUtil
-import com.v2ray.ang.handler.SpeedtestManager
import com.v2ray.ang.util.Utils
import go.Seq
import kotlinx.coroutines.CoroutineScope
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainRecyclerAdapter.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainRecyclerAdapter.kt
index f1b2cb82..05036b2d 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainRecyclerAdapter.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainRecyclerAdapter.kt
@@ -83,7 +83,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter
Date: Thu, 20 Mar 2025 15:36:06 +0800
Subject: [PATCH 072/238] Refactoring to import custom configuration function
Most of the functions have been merged into the existing menu, only the local import function needs to be added.
The code is temporarily commented and will be deleted after user testing.
---
.../java/com/v2ray/ang/ui/MainActivity.kt | 243 ++++++++++--------
.../com/v2ray/ang/viewmodel/MainViewModel.kt | 63 +++--
V2rayNG/app/src/main/res/menu/menu_main.xml | 48 ++--
.../app/src/main/res/values-ar/strings.xml | 1 +
.../app/src/main/res/values-bn/strings.xml | 1 +
.../src/main/res/values-bqi-rIR/strings.xml | 1 +
.../app/src/main/res/values-fa/strings.xml | 1 +
.../app/src/main/res/values-ru/strings.xml | 1 +
.../app/src/main/res/values-vi/strings.xml | 1 +
.../src/main/res/values-zh-rCN/strings.xml | 1 +
.../src/main/res/values-zh-rTW/strings.xml | 1 +
V2rayNG/app/src/main/res/values/strings.xml | 1 +
12 files changed, 195 insertions(+), 168 deletions(-)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt
index 25618841..5139d45c 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt
@@ -8,11 +8,9 @@ import android.net.Uri
import android.net.VpnService
import android.os.Build
import android.os.Bundle
-import android.text.TextUtils
import android.view.KeyEvent
import android.view.Menu
import android.view.MenuItem
-import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
@@ -38,14 +36,12 @@ import com.v2ray.ang.handler.MigrateManager
import com.v2ray.ang.handler.MmkvManager
import com.v2ray.ang.helper.SimpleItemTouchHelperCallback
import com.v2ray.ang.service.V2RayServiceManager
-import com.v2ray.ang.util.HttpUtil
import com.v2ray.ang.util.Utils
import com.v2ray.ang.viewmodel.MainViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
-import me.drakeet.support.toast.ToastCompat
class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedListener {
private val binding by lazy {
@@ -87,13 +83,16 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
when (pendingAction) {
Action.IMPORT_QR_CODE_CONFIG ->
scanQRCodeForConfig.launch(Intent(this, ScannerActivity::class.java))
- Action.IMPORT_QR_CODE_URL ->
- scanQRCodeForUrlToCustomConfig.launch(Intent(this, ScannerActivity::class.java))
+
+// Action.IMPORT_QR_CODE_URL ->
+// scanQRCodeForUrlToCustomConfig.launch(Intent(this, ScannerActivity::class.java))
+
Action.READ_CONTENT_FROM_URI ->
chooseFileForCustomConfig.launch(Intent.createChooser(Intent(Intent.ACTION_GET_CONTENT).apply {
type = "*/*"
addCategory(Intent.CATEGORY_OPENABLE)
}, getString(R.string.title_file_chooser)))
+
Action.POST_NOTIFICATIONS -> {}
else -> {}
}
@@ -108,7 +107,8 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
enum class Action {
NONE,
IMPORT_QR_CODE_CONFIG,
- IMPORT_QR_CODE_URL,
+
+ //IMPORT_QR_CODE_URL,
READ_CONTENT_FROM_URI,
POST_NOTIFICATIONS
}
@@ -126,11 +126,11 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
}
}
- private val scanQRCodeForUrlToCustomConfig = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
- if (it.resultCode == RESULT_OK) {
- importConfigCustomUrl(it.data?.getStringExtra("SCAN_RESULT"))
- }
- }
+// private val scanQRCodeForUrlToCustomConfig = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
+// if (it.resultCode == RESULT_OK) {
+// importConfigCustomUrl(it.data?.getStringExtra("SCAN_RESULT"))
+// }
+// }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -326,6 +326,11 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
true
}
+ R.id.import_local -> {
+ importConfigLocal()
+ true
+ }
+
R.id.import_manually_vmess -> {
importManually(EConfigType.VMESS.value)
true
@@ -366,25 +371,25 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
true
}
- R.id.import_config_custom_clipboard -> {
- importConfigCustomClipboard()
- true
- }
-
- R.id.import_config_custom_local -> {
- importConfigCustomLocal()
- true
- }
-
- R.id.import_config_custom_url -> {
- importConfigCustomUrlClipboard()
- true
- }
-
- R.id.import_config_custom_url_scan -> {
- importQRcode(false)
- true
- }
+// R.id.import_config_custom_clipboard -> {
+// importConfigCustomClipboard()
+// true
+// }
+//
+// R.id.import_config_custom_local -> {
+// importConfigCustomLocal()
+// true
+// }
+//
+// R.id.import_config_custom_url -> {
+// importConfigCustomUrlClipboard()
+// true
+// }
+//
+// R.id.import_config_custom_url_scan -> {
+// importQRcode(false)
+// true
+// }
R.id.sub_update -> {
importConfigViaSub()
@@ -517,10 +522,10 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
if (forConfig) {
scanQRCodeForConfig.launch(Intent(this, ScannerActivity::class.java))
} else {
- scanQRCodeForUrlToCustomConfig.launch(Intent(this, ScannerActivity::class.java))
+ //scanQRCodeForUrlToCustomConfig.launch(Intent(this, ScannerActivity::class.java))
}
} else {
- pendingAction = if (forConfig) Action.IMPORT_QR_CODE_CONFIG else Action.IMPORT_QR_CODE_URL
+ pendingAction = Action.IMPORT_QR_CODE_CONFIG//if (forConfig) Action.IMPORT_QR_CODE_CONFIG else Action.IMPORT_QR_CODE_URL
requestPermissionLauncher.launch(permission)
}
return true
@@ -570,27 +575,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
}
}
-
- private fun importConfigCustomClipboard()
- : Boolean {
- try {
- val configText = Utils.getClipboard(this)
- if (TextUtils.isEmpty(configText)) {
- toast(R.string.toast_none_data_clipboard)
- return false
- }
- importCustomizeConfig(configText)
- return true
- } catch (e: Exception) {
- e.printStackTrace()
- return false
- }
- }
-
- /**
- * import config from local config file
- */
- private fun importConfigCustomLocal(): Boolean {
+ private fun importConfigLocal(): Boolean {
try {
showFileChooser()
} catch (e: Exception) {
@@ -600,47 +585,77 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
return true
}
- private fun importConfigCustomUrlClipboard()
- : Boolean {
- try {
- val url = Utils.getClipboard(this)
- if (TextUtils.isEmpty(url)) {
- toast(R.string.toast_none_data_clipboard)
- return false
- }
- return importConfigCustomUrl(url)
- } catch (e: Exception) {
- e.printStackTrace()
- return false
- }
- }
+
+// private fun importConfigCustomClipboard()
+// : Boolean {
+// try {
+// val configText = Utils.getClipboard(this)
+// if (TextUtils.isEmpty(configText)) {
+// toast(R.string.toast_none_data_clipboard)
+// return false
+// }
+// importCustomizeConfig(configText)
+// return true
+// } catch (e: Exception) {
+// e.printStackTrace()
+// return false
+// }
+// }
+
+ /**
+ * import config from local config file
+ */
+// private fun importConfigCustomLocal(): Boolean {
+// try {
+// showFileChooser()
+// } catch (e: Exception) {
+// e.printStackTrace()
+// return false
+// }
+// return true
+// }
+//
+// private fun importConfigCustomUrlClipboard()
+// : Boolean {
+// try {
+// val url = Utils.getClipboard(this)
+// if (TextUtils.isEmpty(url)) {
+// toast(R.string.toast_none_data_clipboard)
+// return false
+// }
+// return importConfigCustomUrl(url)
+// } catch (e: Exception) {
+// e.printStackTrace()
+// return false
+// }
+// }
/**
* import config from url
*/
- private fun importConfigCustomUrl(url: String?): Boolean {
- try {
- if (!Utils.isValidUrl(url)) {
- toast(R.string.toast_invalid_url)
- return false
- }
- lifecycleScope.launch(Dispatchers.IO) {
- val configText = try {
- HttpUtil.getUrlContentWithUserAgent(url)
- } catch (e: Exception) {
- e.printStackTrace()
- ""
- }
- launch(Dispatchers.Main) {
- importCustomizeConfig(configText)
- }
- }
- } catch (e: Exception) {
- e.printStackTrace()
- return false
- }
- return true
- }
+// private fun importConfigCustomUrl(url: String?): Boolean {
+// try {
+// if (!Utils.isValidUrl(url)) {
+// toast(R.string.toast_invalid_url)
+// return false
+// }
+// lifecycleScope.launch(Dispatchers.IO) {
+// val configText = try {
+// HttpUtil.getUrlContentWithUserAgent(url)
+// } catch (e: Exception) {
+// e.printStackTrace()
+// ""
+// }
+// launch(Dispatchers.Main) {
+// importCustomizeConfig(configText)
+// }
+// }
+// } catch (e: Exception) {
+// e.printStackTrace()
+// return false
+// }
+// return true
+// }
/**
* import config from sub
@@ -699,7 +714,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
if (ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED) {
try {
contentResolver.openInputStream(uri).use { input ->
- importCustomizeConfig(input?.bufferedReader()?.readText())
+ importBatchConfig(input?.bufferedReader()?.readText())
}
} catch (e: Exception) {
e.printStackTrace()
@@ -709,28 +724,28 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
}
}
- /**
- * import customize config
- */
- private fun importCustomizeConfig(server: String?) {
- try {
- if (server == null || TextUtils.isEmpty(server)) {
- toast(R.string.toast_none_data)
- return
- }
- if (mainViewModel.appendCustomConfigServer(server)) {
- mainViewModel.reloadServerList()
- toast(R.string.toast_success)
- } else {
- toast(R.string.toast_failure)
- }
- //adapter.notifyItemInserted(mainViewModel.serverList.lastIndex)
- } catch (e: Exception) {
- ToastCompat.makeText(this, "${getString(R.string.toast_malformed_josn)} ${e.cause?.message}", Toast.LENGTH_LONG).show()
- e.printStackTrace()
- return
- }
- }
+// /**
+// * import customize config
+// */
+// private fun importCustomizeConfig(server: String?) {
+// try {
+// if (server == null || TextUtils.isEmpty(server)) {
+// toast(R.string.toast_none_data)
+// return
+// }
+// if (mainViewModel.appendCustomConfigServer(server)) {
+// mainViewModel.reloadServerList()
+// toast(R.string.toast_success)
+// } else {
+// toast(R.string.toast_failure)
+// }
+// //adapter.notifyItemInserted(mainViewModel.serverList.lastIndex)
+// } catch (e: Exception) {
+// ToastCompat.makeText(this, "${getString(R.string.toast_malformed_josn)} ${e.cause?.message}", Toast.LENGTH_LONG).show()
+// e.printStackTrace()
+// return
+// }
+// }
private fun setTestState(content: String?) {
binding.tvTestState.text = content
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/MainViewModel.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/MainViewModel.kt
index 22b1d966..290b21a4 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/MainViewModel.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/MainViewModel.kt
@@ -19,7 +19,6 @@ import com.v2ray.ang.dto.ProfileItem
import com.v2ray.ang.dto.ServersCache
import com.v2ray.ang.extension.serializable
import com.v2ray.ang.extension.toast
-import com.v2ray.ang.fmt.CustomFmt
import com.v2ray.ang.handler.AngConfigManager
import com.v2ray.ang.handler.MmkvManager
import com.v2ray.ang.handler.SettingsManager
@@ -89,37 +88,37 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
}
}
- /**
- * Appends a custom configuration server.
- * @param server The server configuration to append.
- * @return True if the server was successfully appended, false otherwise.
- */
- fun appendCustomConfigServer(server: String): Boolean {
- if (server.contains("inbounds")
- && server.contains("outbounds")
- && server.contains("routing")
- ) {
- try {
- val config = CustomFmt.parse(server) ?: return false
- config.subscriptionId = subscriptionId
- val key = MmkvManager.encodeServerConfig("", config)
- MmkvManager.encodeServerRaw(key, server)
- serverList.add(0, key)
-// val profile = ProfileLiteItem(
-// configType = config.configType,
-// subscriptionId = config.subscriptionId,
-// remarks = config.remarks,
-// server = config.getProxyOutbound()?.getServerAddress(),
-// serverPort = config.getProxyOutbound()?.getServerPort(),
-// )
- serversCache.add(0, ServersCache(key, config))
- return true
- } catch (e: Exception) {
- e.printStackTrace()
- }
- }
- return false
- }
+// /**
+// * Appends a custom configuration server.
+// * @param server The server configuration to append.
+// * @return True if the server was successfully appended, false otherwise.
+// */
+// fun appendCustomConfigServer(server: String): Boolean {
+// if (server.contains("inbounds")
+// && server.contains("outbounds")
+// && server.contains("routing")
+// ) {
+// try {
+// val config = CustomFmt.parse(server) ?: return false
+// config.subscriptionId = subscriptionId
+// val key = MmkvManager.encodeServerConfig("", config)
+// MmkvManager.encodeServerRaw(key, server)
+// serverList.add(0, key)
+//// val profile = ProfileLiteItem(
+//// configType = config.configType,
+//// subscriptionId = config.subscriptionId,
+//// remarks = config.remarks,
+//// server = config.getProxyOutbound()?.getServerAddress(),
+//// serverPort = config.getProxyOutbound()?.getServerPort(),
+//// )
+// serversCache.add(0, ServersCache(key, config))
+// return true
+// } catch (e: Exception) {
+// e.printStackTrace()
+// }
+// }
+// return false
+// }
/**
* Swaps the positions of two servers.
diff --git a/V2rayNG/app/src/main/res/menu/menu_main.xml b/V2rayNG/app/src/main/res/menu/menu_main.xml
index ce4b16a4..85db881e 100644
--- a/V2rayNG/app/src/main/res/menu/menu_main.xml
+++ b/V2rayNG/app/src/main/res/menu/menu_main.xml
@@ -20,6 +20,10 @@
android:id="@+id/import_clipboard"
android:title="@string/menu_item_import_config_clipboard"
app:showAsAction="never" />
+
- -
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
- حذف التكوين
استيراد التكوين من رمز الاستجابة السريعة (QRcode)
استيراد التكوين من الحافظة
+ Import config from locally
الكتابة يدويًا [VMess]
الكتابة يدويًا [VLESS]
الكتابة يدويًا [Shadowsocks]
diff --git a/V2rayNG/app/src/main/res/values-bn/strings.xml b/V2rayNG/app/src/main/res/values-bn/strings.xml
index 272ee1ea..73192be5 100644
--- a/V2rayNG/app/src/main/res/values-bn/strings.xml
+++ b/V2rayNG/app/src/main/res/values-bn/strings.xml
@@ -27,6 +27,7 @@
কনফিগারেশন মুছুন
QR কোড থেকে কনফিগারেশন আমদানি করুন
ক্লিপবোর্ড থেকে কনফিগারেশন আমদানি করুন
+ Import config from locally
ম্যানুয়ালি টাইপ করুন [VMess]
ম্যানুয়ালি টাইপ করুন [VLESS]
ম্যানুয়ালি টাইপ করুন [Shadowsocks]
diff --git a/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml b/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml
index 7e31d20f..37159984 100644
--- a/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml
+++ b/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml
@@ -27,6 +27,7 @@
پاک کردن کانفیگ
و من ٱووردن کانفیگ ز QRcode
و من ٱووردن کانفیگ ز کلیپ بورد
+ Import config from locally
هؽل دستی[VMess]
هؽل دستی[VLESS]
هؽل دستی[Shadowsocks]
diff --git a/V2rayNG/app/src/main/res/values-fa/strings.xml b/V2rayNG/app/src/main/res/values-fa/strings.xml
index 7e795239..a3cae007 100644
--- a/V2rayNG/app/src/main/res/values-fa/strings.xml
+++ b/V2rayNG/app/src/main/res/values-fa/strings.xml
@@ -27,6 +27,7 @@
حذف کانفیگ
کانفیگ را از QRcode وارد کنید
کانفیگ را از کلیپ بورد وارد کنید
+ Import config from locally
تایپ دستی[VMESS]
تایپ دستی[VLESS]
تایپ دستی[SHADOWSOCKS]
diff --git a/V2rayNG/app/src/main/res/values-ru/strings.xml b/V2rayNG/app/src/main/res/values-ru/strings.xml
index 52d79a6f..c50432ae 100644
--- a/V2rayNG/app/src/main/res/values-ru/strings.xml
+++ b/V2rayNG/app/src/main/res/values-ru/strings.xml
@@ -26,6 +26,7 @@
Удалить профиль
Импорт из QR-кода
Импорт из буфера обмена
+ Import config from locally
Ручной ввод VMess
Ручной ввод VLESS
Ручной ввод Shadowsocks
diff --git a/V2rayNG/app/src/main/res/values-vi/strings.xml b/V2rayNG/app/src/main/res/values-vi/strings.xml
index f0acddf4..c7666524 100644
--- a/V2rayNG/app/src/main/res/values-vi/strings.xml
+++ b/V2rayNG/app/src/main/res/values-vi/strings.xml
@@ -26,6 +26,7 @@
Xoá cấu hình
Nhập cấu hình từ mã QR
Nhập cấu hình từ Clipboard
+ Import config from locally
Nhập thủ công [VMess]
Nhập thủ công [VLESS]
Nhập thủ công [ShadowSocks]
diff --git a/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml b/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml
index 3542d89f..ef2813e4 100644
--- a/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml
+++ b/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml
@@ -26,6 +26,7 @@
删除配置
扫描二维码
从剪贴板导入
+ 从本地导入
手动输入[VMess]
手动输入[VLESS]
手动输入[Shadowsocks]
diff --git a/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml b/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml
index 05fa03b5..ecdb5d3d 100644
--- a/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml
+++ b/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml
@@ -26,6 +26,7 @@
刪除設定
從 QR Code 匯入設定
從剪貼簿匯入設定
+ 從本地匯入
手動鍵入 [VMess]
手動鍵入 [VLESS]
手動鍵入 [Shadowsocks]
diff --git a/V2rayNG/app/src/main/res/values/strings.xml b/V2rayNG/app/src/main/res/values/strings.xml
index efdee8df..25aba52b 100644
--- a/V2rayNG/app/src/main/res/values/strings.xml
+++ b/V2rayNG/app/src/main/res/values/strings.xml
@@ -27,6 +27,7 @@
Delete config
Import config from QRcode
Import config from Clipboard
+ Import config from locally
Type manually[VMess]
Type manually[VLESS]
Type manually[Shadowsocks]
From 1bc433097b597c008f3b489be60436d6b1518a7a Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Thu, 20 Mar 2025 16:14:41 +0800
Subject: [PATCH 073/238] Add per_app_proxy_settings to the drawer menu
---
.../src/main/java/com/v2ray/ang/ui/MainActivity.kt | 5 ++++-
.../src/main/res/drawable-night/ic_per_apps_24dp.xml | 11 +++++++++++
.../app/src/main/res/drawable/ic_per_apps_24dp.xml | 11 +++++++++++
V2rayNG/app/src/main/res/menu/menu_drawer.xml | 4 ++++
4 files changed, 30 insertions(+), 1 deletion(-)
create mode 100644 V2rayNG/app/src/main/res/drawable-night/ic_per_apps_24dp.xml
create mode 100644 V2rayNG/app/src/main/res/drawable/ic_per_apps_24dp.xml
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt
index 5139d45c..a6ca521d 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt
@@ -783,11 +783,14 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
)
}
+ R.id.per_app_proxy_settings -> {
+ startActivity(Intent(this, PerAppProxyActivity::class.java))
+ }
+
R.id.routing_setting -> {
requestSubSettingActivity.launch(Intent(this, RoutingSettingActivity::class.java))
}
-
R.id.promotion -> {
Utils.openUri(this, "${Utils.decode(AppConfig.PromotionUrl)}?t=${System.currentTimeMillis()}")
}
diff --git a/V2rayNG/app/src/main/res/drawable-night/ic_per_apps_24dp.xml b/V2rayNG/app/src/main/res/drawable-night/ic_per_apps_24dp.xml
new file mode 100644
index 00000000..99d8212e
--- /dev/null
+++ b/V2rayNG/app/src/main/res/drawable-night/ic_per_apps_24dp.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
diff --git a/V2rayNG/app/src/main/res/drawable/ic_per_apps_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_per_apps_24dp.xml
new file mode 100644
index 00000000..c45cd8c5
--- /dev/null
+++ b/V2rayNG/app/src/main/res/drawable/ic_per_apps_24dp.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
diff --git a/V2rayNG/app/src/main/res/menu/menu_drawer.xml b/V2rayNG/app/src/main/res/menu/menu_drawer.xml
index 3c739e1b..a9ddcb1d 100644
--- a/V2rayNG/app/src/main/res/menu/menu_drawer.xml
+++ b/V2rayNG/app/src/main/res/menu/menu_drawer.xml
@@ -12,6 +12,10 @@
android:id="@+id/settings"
android:icon="@drawable/ic_settings_24dp"
android:title="@string/title_settings" />
+
-
Date: Thu, 20 Mar 2025 20:16:54 +0800
Subject: [PATCH 074/238] Optimize and improve string
---
.../com/v2ray/ang/ui/PerAppProxyActivity.kt | 4 +-
.../main/res/layout/activity_bypass_list.xml | 2 +-
.../res/layout/item_recycler_bypass_list.xml | 2 +-
V2rayNG/app/src/main/res/menu/menu_drawer.xml | 2 +-
.../app/src/main/res/values-ar/strings.xml | 24 ++-
.../app/src/main/res/values-bn/strings.xml | 24 ++-
.../src/main/res/values-bqi-rIR/strings.xml | 24 ++-
.../app/src/main/res/values-fa/strings.xml | 18 +-
.../app/src/main/res/values-ru/strings.xml | 24 ++-
.../app/src/main/res/values-vi/strings.xml | 18 +-
.../src/main/res/values-zh-rCN/strings.xml | 156 +++++++++---------
.../src/main/res/values-zh-rTW/strings.xml | 52 +++---
V2rayNG/app/src/main/res/values/strings.xml | 29 ++--
13 files changed, 216 insertions(+), 163 deletions(-)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/PerAppProxyActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/PerAppProxyActivity.kt
index e286034b..3cffda02 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/PerAppProxyActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/PerAppProxyActivity.kt
@@ -36,9 +36,9 @@ class PerAppProxyActivity : BaseActivity() {
super.onCreate(savedInstanceState)
setContentView(binding.root)
- addCustomDividerToRecyclerView(binding.recyclerView, this, R.drawable.custom_divider)
+ title = getString(R.string.per_app_proxy_settings)
- val blacklist = MmkvManager.decodeSettingsStringSet(AppConfig.PREF_PER_APP_PROXY_SET)
+ addCustomDividerToRecyclerView(binding.recyclerView, this, R.drawable.custom_divider)
lifecycleScope.launch {
try {
diff --git a/V2rayNG/app/src/main/res/layout/activity_bypass_list.xml b/V2rayNG/app/src/main/res/layout/activity_bypass_list.xml
index e40edd72..90455574 100644
--- a/V2rayNG/app/src/main/res/layout/activity_bypass_list.xml
+++ b/V2rayNG/app/src/main/res/layout/activity_bypass_list.xml
@@ -47,7 +47,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
- android:text="@string/title_pref_per_app_proxy"
+ android:text="@string/per_app_proxy_settings_enable"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textColor="@color/colorAccent"
app:theme="@style/BrandedSwitch" />
diff --git a/V2rayNG/app/src/main/res/layout/item_recycler_bypass_list.xml b/V2rayNG/app/src/main/res/layout/item_recycler_bypass_list.xml
index b2d62b6f..1ca2e9de 100644
--- a/V2rayNG/app/src/main/res/layout/item_recycler_bypass_list.xml
+++ b/V2rayNG/app/src/main/res/layout/item_recycler_bypass_list.xml
@@ -42,6 +42,6 @@
android:layout_height="wrap_content"
android:clickable="false"
android:focusable="false"
- android:padding="@dimen/padding_spacing_dp16"/>
+ android:paddingEnd="@dimen/padding_spacing_dp16"/>
\ No newline at end of file
diff --git a/V2rayNG/app/src/main/res/menu/menu_drawer.xml b/V2rayNG/app/src/main/res/menu/menu_drawer.xml
index a9ddcb1d..a2b17dd1 100644
--- a/V2rayNG/app/src/main/res/menu/menu_drawer.xml
+++ b/V2rayNG/app/src/main/res/menu/menu_drawer.xml
@@ -15,7 +15,7 @@
+ android:title="@string/per_app_proxy_settings" />
- تأكد من أن منفذ الاتصالات الواردة يتوافق مع الإعدادات
تكوين مشوه
مضيف (SNI) (اختياري)
+ الإجراء غير مسموح به
+ Obfs password
+ Port Hopping
+ Port Hopping Interval
+ pinSHA256
+ Bandwidth down (units)
+ Bandwidth up (units)
+ XHTTP Mode
+ XHTTP Extra raw JSON, format: { XHTTPObject }
+
+
فشل نسخ الملف، يرجى استخدام مدير الملفات
إضافة أصل
إضافة ملفات
@@ -117,15 +128,6 @@
إضافة عنوان URL للأصل
الملف غير موجود
الملاحظات موجودة بالفعل
- الإجراء غير مسموح به
- Obfs password
- Port Hopping
- Port Hopping Interval
- pinSHA256
- Bandwidth down (units)
- Bandwidth up (units)
- XHTTP Mode
- XHTTP Extra raw JSON, format: { XHTTPObject }
جار التحميل
@@ -137,6 +139,8 @@
جارٍ تنزيل المحتوى
تصدير إلى الحافظة
استيراد من الحافظة
+ Per-app settings
+ Enable per-app
@@ -213,6 +217,7 @@
Append HTTP Proxy to VPN
HTTP proxy will be used directly from (browser/ some supported apps), without going through the virtual NIC device (Android 10+)
+
ملاحظات
ملاحظات التحسينات أو الأخطاء إلى GitHub
الانضمام إلى مجموعة Telegram
@@ -274,6 +279,7 @@
بدء الخدمة
تأكيد
+
استراتيجية النطاق
إعدادات التوجيه
مفصولة بفواصل (،)، تذكر الحفظ
diff --git a/V2rayNG/app/src/main/res/values-bn/strings.xml b/V2rayNG/app/src/main/res/values-bn/strings.xml
index 73192be5..970689e3 100644
--- a/V2rayNG/app/src/main/res/values-bn/strings.xml
+++ b/V2rayNG/app/src/main/res/values-bn/strings.xml
@@ -106,6 +106,17 @@
ইনবাউন্ড পোর্ট নিশ্চিত করুন সেটিংসের সাথে সামঞ্জস্যপূর্ণ
কনফিগারেশন বিকৃত
হোস্ট (SNI) (ঐচ্ছিক)
+ অ্যাকশন অনুমোদিত নয়
+ Obfs password
+ Port Hopping
+ Port Hopping Interval
+ pinSHA256
+ Bandwidth down (units)
+ Bandwidth up (units)
+ XHTTP Mode
+ XHTTP Extra raw JSON, format: { XHTTPObject }
+
+
ফাইল কপি ব্যর্থ, অনুগ্রহ করে ফাইল ম্যানেজার ব্যবহার করুন
অ্যাসেট যোগ করুন
ফাইল যোগ করুন
@@ -116,15 +127,6 @@
অ্যাসেট URL যোগ করুন
ফাইল খুঁজে পাওয়া যায়নি
মন্তব্য ইতিমধ্যে বিদ্যমান
- অ্যাকশন অনুমোদিত নয়
- Obfs password
- Port Hopping
- Port Hopping Interval
- pinSHA256
- Bandwidth down (units)
- Bandwidth up (units)
- XHTTP Mode
- XHTTP Extra raw JSON, format: { XHTTPObject }
লোড হচ্ছে
@@ -136,6 +138,8 @@
বিষয়বস্তু ডাউনলোড হচ্ছে
ক্লিপবোর্ডে রপ্তানি করুন
ক্লিপবোর্ড থেকে আমদানি করুন
+ Per-app settings
+ Enable per-app
সেটিংস
@@ -213,6 +217,7 @@
Append HTTP Proxy to VPN
HTTP proxy will be used directly from (browser/ some supported apps), without going through the virtual NIC device (Android 10+)
+
মতামত
মতামত উন্নয়ন বা বাগগুলি GitHub-এ পাঠান
টেলিগ্রাম গ্রুপে যোগদান করুন
@@ -273,6 +278,7 @@
সার্ভিস শুরু করুন
নিশ্চিত করুন
+
ডোমেইন কৌশল
রাউটিং সেটিংস
কমা (,) দ্বারা আলাদা করুন, মনে রাখবেন সেভ করতে
diff --git a/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml b/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml
index 37159984..99b04144 100644
--- a/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml
+++ b/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml
@@ -106,6 +106,17 @@
موتمعن بۊین ک پورت وۊرۊڌی وا سامووا ی جۊر هڌ
کانفیگ زبال نؽڌ
هاست(SNI)(اختیاری)
+ ای کار ممنۊ هڌ
+ رزم obfs
+ پورت گوم (درگا سرورن ز نۊ هؽل اکونه)
+ فاسله پورت گوم (سانیه)
+ pinSHA256
+ ب لم ٱووڌن پئنا باند (واهڌ)
+ وا روء رئڌن پئنا باند (واهڌ)
+ هالت XHTTP
+ XHTTP Extra خام JSON، قالوو: { XHTTPObject }
+
+
لف گیری فایل ٱنجوم نوابی، ز ی برنومه دؽوۉداری فایل هیاری بگرین
ازاف کردن دارایی
ازاف کردن فایل
@@ -116,15 +127,6 @@
آدرس اینترنتی دارایین ازاف کۊنین
فایلن نجوست
ائزارات ز زیتر بیڌسۉݩ
- ای کار ممنۊ هڌ
- رزم obfs
- پورت گوم (درگا سرورن ز نۊ هؽل اکونه)
- فاسله پورت گوم (سانیه)
- pinSHA256
- ب لم ٱووڌن پئنا باند (واهڌ)
- وا روء رئڌن پئنا باند (واهڌ)
- هالت XHTTP
- XHTTP Extra خام JSON، قالوو: { XHTTPObject }
هون بار ونی بۊ
@@ -136,6 +138,8 @@
موئتوا هونی دانلود ابۊن
و در کشیڌن من کلیپ بورد
و من ٱووردن ز کلیپ بورد
+ Per-app settings
+ Enable per-app
@@ -213,6 +217,7 @@
پروکسی HTTP ن و VPN ازاف کۊنین
پروکسی HTTP ن موسقیمن ز (مۊرۊرگر/ی قرد ز برنومیل لادراری بیڌه)، بؽ استفاڌه ز دسگا NIC مجازی (Android 10+) استفاڌه ابۊ.
+
فشناڌن منشڌ
فشناڌن منشڌ یا داسوو موشکلا من Github
ٱووڌن من جرگه تلگرام
@@ -274,6 +279,7 @@
ر وندن خدمات
قوۊل
+
نشقه دامنه
سامووا تور جوستن
وا کاما ز یک جوڌا ابۊن (،) پسند دامنه یا آی پی
diff --git a/V2rayNG/app/src/main/res/values-fa/strings.xml b/V2rayNG/app/src/main/res/values-fa/strings.xml
index a3cae007..77815bff 100644
--- a/V2rayNG/app/src/main/res/values-fa/strings.xml
+++ b/V2rayNG/app/src/main/res/values-fa/strings.xml
@@ -106,11 +106,6 @@
اطمینان حاصل کنید که پورت ورودی با تنظیمات مطابقت دارد
کانفیگ درست نیست
هاست (SNI) (اختیاری)
- کپی فایل انجام نشد، لطفا از برنامه مدیریت فایل استفاده کنید
- افزودن فایل ها
- اسکن QRcode
- URL
- دانلود فایل ها
این عمل ممنوع است
رمز عبور obfs
پورت پرش (درگاه سرور را بازنویسی می کند)
@@ -121,10 +116,17 @@
حالت XHTTP
خام JSON XHTTP Extra، قالب: { XHTTPObject }
-
+
+ کپی فایل انجام نشد، لطفا از برنامه مدیریت فایل استفاده کنید
+ افزودن فایل ها
+ اسکن QRcode
+ URL
+ دانلود فایل ها
آدرس اینترنتی را اضافه کنید
فایل پیدا نشد
نام قبلاً وجود دارد
+
+
بارگذاری
جستجو
انتخاب همه
@@ -134,6 +136,8 @@
در حال دانلود محتوا
خروجی گرفتن در کلیپ بورد
وارد کردن از کلیپ بورد
+ Per-app settings
+ Enable per-app
تنظیمات
@@ -211,6 +215,7 @@
پروکسی HTTP را به VPN اضافه کنید
پروکسی HTTP مستقیماً از (مرورگر/برخی برنامههای پشتیبانیشده)، بدون استفاده از دستگاه NIC مجازی (Android 10+) استفاده میشود.
+
بازخورد
بازخورد یا گزارش اشکالات در گیت هاب
عضویت در گروه تلگرام
@@ -271,6 +276,7 @@
شروع خدمات
تایید
+
استراتژی دامنه
تنظیمات مسیریابی
با کاما (،) از هم جدا شوند، ذخیره کردن فراموش نشود
diff --git a/V2rayNG/app/src/main/res/values-ru/strings.xml b/V2rayNG/app/src/main/res/values-ru/strings.xml
index c50432ae..05a453fc 100644
--- a/V2rayNG/app/src/main/res/values-ru/strings.xml
+++ b/V2rayNG/app/src/main/res/values-ru/strings.xml
@@ -105,6 +105,17 @@
Убедитесь, что входящий порт соответствует настройкам
Профиль повреждён
Узел (SNI) (необязательно)
+ Это действие запрещено
+ Пароль obfs
+ Смена портов (переопределяет порт)
+ Интервал смены портов
+ pinSHA256
+ Входящая пропускная способность (единицы)
+ Исходящая пропускная способность (единицы)
+ Режим XHTTP
+ Необработанный JSON XHTTP Extra, формат: { XHTTPObject }
+
+
Невозможно скопировать файл, используйте файловый менеджер
Добавить ресурс
Добавить файлы
@@ -115,15 +126,6 @@
Добавить URL ресурса
Файл не найден
Название уже существует
- Это действие запрещено
- Пароль obfs
- Смена портов (переопределяет порт)
- Интервал смены портов
- pinSHA256
- Входящая пропускная способность (единицы)
- Исходящая пропускная способность (единицы)
- Режим XHTTP
- Необработанный JSON XHTTP Extra, формат: { XHTTPObject }
Загрузка…
@@ -135,6 +137,8 @@
Загрузка данных
Экспорт в буфер обмена
Импорт из буфера обмена
+ Per-app settings
+ Enable per-app
@@ -212,6 +216,7 @@
Дополнительный HTTP-прокси
HTTP-прокси будет использоваться напрямую (из браузера и других поддерживающих приложений), минуя виртуальный сетевой адаптер (Android 10+)
+
Обратная связь
Предложить улучшение или сообщить об ошибке на GitHub
Присоединиться к группе в Telegram
@@ -273,6 +278,7 @@
Запуск службы
Подтвердить
+
Доменная стратегия
Маршрутизация
Введите требуемые домены/IP через запятую
diff --git a/V2rayNG/app/src/main/res/values-vi/strings.xml b/V2rayNG/app/src/main/res/values-vi/strings.xml
index c7666524..4f3d0294 100644
--- a/V2rayNG/app/src/main/res/values-vi/strings.xml
+++ b/V2rayNG/app/src/main/res/values-vi/strings.xml
@@ -105,11 +105,6 @@
Vui lòng đảm bảo cấu hình tùy chỉnh này không bị lỗi trước khi sử dụng!
Cấu hình không hợp lệ!
Host (SNI) (Không bắt buộc)
- Không thể sao chép tệp tin, hãy dùng trình quản lý tệp!
- Thêm tệp
- Quét mã QR
- URL
- Tải xuống tệp tin
Hành động này bị cấm!
Obfs password
Port Hopping
@@ -120,10 +115,17 @@
XHTTP Mode
XHTTP Extra raw JSON, format: { XHTTPObject }
-
+
+ Không thể sao chép tệp tin, hãy dùng trình quản lý tệp!
+ Thêm tệp
+ Quét mã QR
+ URL
+ Tải xuống tệp tin
Thêm URL nội dung
Không tìm thấy tập tin!
Nhận xét đã tồn tại!
+
+
Đang tải...
Tìm kiếm
Chọn tất cả
@@ -133,6 +135,8 @@
Đang tải xuống nội dung...
Xuất và Sao chép
Nhập từ Clipboard
+ Per-app settings
+ Enable per-app
@@ -212,6 +216,7 @@
Append HTTP Proxy to VPN
HTTP proxy will be used directly from (browser/ some supported apps), without going through the virtual NIC device (Android 10+)
+
Phản hồi lỗi
Phản hồi cải tiến hoặc lỗi lên GitHub
Tham gia nhóm Telegram
@@ -273,6 +278,7 @@
Khởi động v2rayNG
Xác nhận
+
Chiến lược tên miền (DomainStrategy)
Cài đặt định tuyến
Phân cách bằng dấu phẩy (,). Có thể tải xuống Rules mặc định để tham khảo ở menu ba chấm.
diff --git a/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml b/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml
index ef2813e4..6ec73453 100644
--- a/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml
+++ b/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml
@@ -2,7 +2,7 @@
开关
开关
- 初次使用此功能请先用APP添加配置
+ 初次使用此功能请先用 APP 添加配置
Open navigation drawer
Close navigation drawer
数据迁移成功!
@@ -27,32 +27,32 @@
扫描二维码
从剪贴板导入
从本地导入
- 手动输入[VMess]
- 手动输入[VLESS]
- 手动输入[Shadowsocks]
- 手动输入[SOCKS]
- 手动输入[HTTP]
- 手动输入[Trojan]
- 手动输入[Wireguard]
- 手动输入[Hysteria2]
+ 手动输入 [VMess]
+ 手动输入 [VLESS]
+ 手动输入 [Shadowsocks]
+ 手动输入 [SOCKS]
+ 手动输入 [HTTP]
+ 手动输入 [Trojan]
+ 手动输入 [Wireguard]
+ 手动输入 [Hysteria2]
自定义配置
从剪贴板导入自定义配置
从本地导入自定义配置
- 剪贴板URL导入自定义配置
- 扫描URL导入自定义配置
+ 剪贴板 URL 导入自定义配置
+ 扫描 URL 导入自定义配置
确认删除?
删除前请先测试!确认删除?
- 别名(remarks)
- 地址(address)
- 端口(port)
- 用户ID(id)
- 额外ID(alterId)
- 加密方式(security)
- 传输协议(network)
- 底层传输方式(transport)
- 伪装类型(type)
- gRPC 传输模式(mode)
- 伪装域名(host)
+ 别名 (remarks)
+ 地址 (address)
+ 端口 (port)
+ 用户 ID (id)
+ 额外 ID (alterId)
+ 加密方式 (security)
+ 传输协议 (network)
+ 底层传输方式 (transport)
+ 伪装类型 (type)
+ gRPC 传输模式 (mode)
+ 伪装域名 (host)
http host
ws host
httpupgrade host
@@ -63,32 +63,32 @@
path
ws path
httpupgrade path
- xhttp path
+ xhttp path
h2 path
QUIC 加密密钥
kcp seed
gRPC serviceName
- 传输层安全(TLS)
+ 传输层安全 (TLS)
Fingerprint
Alpn
- 跳过证书验证(allowInsecure)
+ 跳过证书验证 (allowInsecure)
SNI
服务器地址
服务器端口
密码
加密方式
- 密码(可选)
- 用户名(可选)
- 加密方式(encryption)
- 流控(flow)
+ 密码 (可选)
+ 用户名 (可选)
+ 加密方式 (encryption)
+ 流控 (flow)
PublicKey
- PreSharedKey(optional)
+ PreSharedKey (optional)
ShortId
SpiderX
SecretKey
- Reserved(可选,逗号隔开)
- 本地地址(可选IPv4/IPv6,逗号隔开)
- Mtu(可选, 默认1420)
+ Reserved (可选,逗号隔开)
+ 本地地址 (可选 IPv4/IPv6,逗号隔开)
+ Mtu (可选, 默认 1420)
成功
失败
没有数据
@@ -101,29 +101,31 @@
内容
剪贴板中没有数据
无效的网址
- 请不要使用不安全的HTTP协议订阅地址
- 确保inbounds port和设置中的一致
+ 请不要使用不安全的 HTTP 协议订阅地址
+ 确保 inbounds port 和设置中的一致
配置格式错误
- Host(SNI)(可选)
- 失败, 请使用文件管理器
- 添加文件
- 扫描 QRcode
- URL
- 下载文件
+ Host (SNI) (可选)
禁止此项操作
混淆密码
- 跳跃端口(会覆盖服务器端口)
- 端口跳跃间隔(秒)
- SHA256证书指纹
+ 跳跃端口 (会覆盖服务器端口)
+ 端口跳跃间隔 (秒)
+ SHA256 证书指纹
带宽下行 (单位)
带宽上行 (单位)
XHTTP 模式
XHTTP Extra 原始 JSON,格式: { XHTTPObject }
-
+
+ 失败, 请使用文件管理器
+ 添加文件
+ 扫描 QRcode
+ URL
+ 下载文件
添加资产网址
文件未找到
备注已经存在
+
+
正在加载
搜索
全选
@@ -133,13 +135,15 @@
正在下载内容
导出至剪贴板
从剪贴板导入
+ 分应用设置
+ 启用分应用
设置
进阶设置
VPN 设置
- 分应用代理
- 常规:勾选的App被代理,未勾选的直连;\n绕行模式:勾选的App直连,未勾选的被代理.\n不明白者在菜单中选择自动选中需代理应用
+ 分应用
+ 常规: 勾选的 App 被代理, 未勾选的直连;\n绕行模式: 勾选的 App 直连, 未勾选的被代理.\n不明白者在菜单中选择自动选中需代理应用
开机时自动连接
开机时自动连接选择的服务器,可能会不成功
@@ -163,42 +167,42 @@
启用 routeOnly
将嗅探得到的域名仅用于路由,代理目标地址仍为 IP
- 启用本地DNS
+ 启用本地 DNS
DNS 请求导入 core 由 DNS 模块处理(推荐启用 如果需要路由绕过局域网及大陆地址)
- 启用虚拟DNS
+ 启用虚拟 DNS
本地返回虚构解析结果 (减低延时 但个别应用可能无法使用)
- IPv6优先
- App优先使用IPv6地址连接服务器,同时开启VPN的IPv6路由
+ IPv6 优先
+ App 优先使用 IPv6 地址连接服务器, 同时开启 VPN 的 IPv6 路由
- 远程DNS (udp/tcp/https/quic)(可选)
+ 远程 DNS (udp/tcp/https/quic)(可选)
DNS
VPN DNS (仅支持 IPv4/v6)
- VPN是否绕过局域网
+ VPN 是否绕过局域网
- 境内DNS (可选)
+ 境内 DNS (可选)
DNS
- DNS hosts (格式: 域名:地址,…)
- domain:address,…
+ DNS hosts (格式: 域名: 地址,…)
+ domain: address,…
真连接延迟测试网址 (http/https)
Url
允许来自局域网的连接
- 其他设备可以使用socks/http协议通过您的IP地址连接到代理,仅在受信任的网络中启用以避免未经授权的连接
+ 其他设备可以使用 socks/http 协议通过您的 IP 地址连接到代理, 仅在受信任的网络中启用以避免未经授权的连接
允许来自局域网的连接,请确保处于受信网络
- 跳过证书验证(allowInsecure)
- 传输层安全选tls时,默认跳过证书验证(allowInsecure)
+ 跳过证书验证 (allowInsecure)
+ 传输层安全选 tls 时,默认跳过证书验证 (allowInsecure)
本地代理端口
本地代理端口
- 本地DNS端口
- 本地DNS端口
+ 本地 DNS 端口
+ 本地 DNS 端口
删除配置文件确认
删除配置文件是否需要用户二次确认
@@ -209,26 +213,27 @@
追加 HTTP 代理至 VPN
浏览器 / 一些支持的应用 将直接使用 HTTP 代理, 而不经过虚拟网卡设备 (Android 10+)
+
反馈
反馈改进或漏洞至 GitHub
- 加入Telegram Group
- 未找到Telegram app
+ 加入 Telegram Group
+ 未找到 Telegram app
隐私权政策
关于
源代码
Open Source licenses
Telegram 频道
备份配置
- 存储位置: [%s], 卸载App或清除存储后备份将被清除
+ 存储位置: [%s], 卸载 App 或清除存储后备份将被清除
还原配置
分享配置
推广
- 一些推广,点击查看详情(捐赠可去除)
+ 一些推广, 点击查看详情 (捐赠可去除)
自动更新订阅
在后台按一定时间间隔自动更新您的订阅。受设备影响,此功能不一定总是有效
- 自动更新间隔(分钟,最小值15)
+ 自动更新间隔(分钟,最小值 15)
日志级别
模式
@@ -247,7 +252,7 @@
导出当前组配置至剪贴板
订阅分组设置
备注
- 可选地址(url)
+ 可选地址 (url)
别名正则过滤
启用更新
启用自动更新
@@ -255,7 +260,7 @@
落地代理別名
请确保别名存在并唯一
更新当前组订阅
- 测试当前组配置Tcping
+ 测试当前组配置 Tcping
测试当前组配置真连接
Geo 资源文件
按测试结果排序
@@ -270,9 +275,10 @@
启动服务
确定
+
域名策略
路由设置
- 用逗号(,)隔开,domain和ip二选一填写
+ 用逗号(,)隔开, domain 和 ip 二选一填写
保存
清空
路由规则设置
@@ -300,8 +306,8 @@
添加链接
分片(Fragment) 设置
分片方式
- 分片包长(最小-最大)
- 分片间隔(最小-最大)
+ 分片包长(最小 - 最大)
+ 分片间隔(最小 - 最大)
启用分片(Fragment)
@@ -327,10 +333,10 @@
- - 绕过大陆(Whitelist)
- - 黑名单(Blacklist)
- - 全局(Global)
- - 伊朗(Iran)
+ - 绕过大陆 (Whitelist)
+ - 黑名单 (Blacklist)
+ - 全局 (Global)
+ - 伊朗 (Iran)
diff --git a/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml b/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml
index ecdb5d3d..7541fbf7 100644
--- a/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml
+++ b/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml
@@ -19,7 +19,7 @@
啟動服務成功
啟動服務失敗
-
+
設定檔
新增設定
儲存設定
@@ -63,7 +63,7 @@
path
ws path
httpupgrade path
- xhttp path
+ xhttp path
h2 path
QUIC 加密金鑰
kcp seed
@@ -82,13 +82,13 @@
加密 (encryption)
流程 (flow)
PublicKey
- PreSharedKey(optional)
+ PreSharedKey (optional)
ShortId
SpiderX
SecretKey
Reserved (可選,逗號隔開)
- 本機位址(可選IPv4/IPv6,逗號隔開)
- MTU(可選, 預設1420)
+ 本機位址 (可選 IPv4/IPv6,逗號隔開)
+ MTU (可選, 預設 1420)
成功
失敗
無資料
@@ -101,29 +101,31 @@
內容
剪貼簿內無資料
URL 無效
- 請不要使用不安全的HTTP協定訂閱位址
+ 請不要使用不安全的 HTTP 協定訂閱位址
確保 inbounds port 和設定中的一致
設定格式不正確
- Host(SNI)(可選)
- 失敗,請使用檔案總管
- 新增檔案
- 掃描 QRcode
- URL
- 下載檔案
+ Host (SNI) (可選)
禁止此項操作
混淆密碼
- 跳躍連接埠(會覆蓋伺服器連接埠)
- 連接埠跳躍間隔(秒)
- SHA256憑證指紋
+ 跳躍連接埠 (會覆蓋伺服器連接埠)
+ 連接埠跳躍間隔 (秒)
+ SHA256 憑證指紋
頻寬下行 (單位)
頻寬上行 (單位)
XHTTP 模式
XHTTP Extra 原始 JSON,格式: { XHTTPObject }
-
+
+ 失敗,請使用檔案總管
+ 新增檔案
+ 掃描 QRcode
+ URL
+ 下載檔案
新增資產網址
文件未找到
備註已經存在
+
+
載入
搜尋
全選
@@ -133,6 +135,8 @@
正在下載內容
匯出至剪貼簿
從剪貼簿匯入
+ Per-app settings
+ Enable per-app
@@ -174,11 +178,11 @@
IPv6 偏好
App 優先使用 IPv6 位址連線伺服器,同时開啟 VPN 的 IPv6 路由
- 遠端DNS (udp/tcp/https/quic)(可選)
+ 遠端 DNS (udp/tcp/https/quic)(可選)
DNS
VPN DNS (僅支援 IPv4/v6)
- VPN是否繞過區域網
+ VPN 是否繞過區域網
DNS hosts (格式: 網域:位址,…)
domain:address,…
@@ -208,6 +212,7 @@
追加 HTTP 代理至 VPN
瀏覽器 / 一些支援的應用 將直接使用 HTTP 代理, 而不經過虛擬網卡設備 (Android 10+)
+
意見回饋
前往 GitHub 回報錯誤
加入 Telegram 群組
@@ -227,7 +232,7 @@
自動更新訂閱
在後台以一定時間間隔自動更新您的訂閱。受設備影響,此功能不一定總是有效
- 自動更新間隔(分鐘,最小值15)
+ 自動更新間隔(分鐘,最小值 15)
記錄層級
模式
@@ -269,6 +274,7 @@
啟動服務
確定
+
網域策略
轉送設定
以半形逗號「,」分隔,domain和ip二選一填寫
@@ -326,10 +332,10 @@
- - 繞過大陸(Whitelist)
- - 黑名單(Blacklist)
- - 全域(Global)
- - 伊朗(Iran)
+ - 繞過大陸 (Whitelist)
+ - 黑名單 (Blacklist)
+ - 全域 (Global)
+ - 伊朗 (Iran)
diff --git a/V2rayNG/app/src/main/res/values/strings.xml b/V2rayNG/app/src/main/res/values/strings.xml
index 25aba52b..c45f4062 100644
--- a/V2rayNG/app/src/main/res/values/strings.xml
+++ b/V2rayNG/app/src/main/res/values/strings.xml
@@ -14,7 +14,7 @@
Stop
Unable to obtain the permission
Unable to obtain the notification permission
- click for more
+ Click for more
Start Services
Stop Services
Start Services Success
@@ -64,7 +64,7 @@
path
ws path
httpupgrade path
- xhttp path
+ xhttp path
h2 path
QUIC key
kcp seed
@@ -106,6 +106,17 @@
Ensure inbounds port is consistent with the settings
Config malformed
Host(SNI)(Optional)
+ Action not allowed
+ Obfs password
+ Port Hopping(will override the port)
+ Port Hopping Interval
+ pinSHA256
+ Bandwidth down (units)
+ Bandwidth up (units)
+ XHTTP Mode
+ XHTTP Extra raw JSON, format: { XHTTPObject }
+
+
File copy failed, please use File Manager
Add asset
Add files
@@ -116,15 +127,6 @@
Add asset URL
File not found
The remarks already exists
- Action not allowed
- Obfs password
- Port Hopping(will override the port)
- Port Hopping Interval
- pinSHA256
- Bandwidth down (units)
- Bandwidth up (units)
- XHTTP Mode
- XHTTP Extra raw JSON, format: { XHTTPObject }
Loading
@@ -136,7 +138,8 @@
Downloading content
Export to Clipboard
Import from Clipboard
-
+ Per-app settings
+ Enable per-app
Settings
@@ -215,6 +218,7 @@
Append HTTP Proxy to VPN
HTTP proxy will be used directly from (browser/ some supported apps), without going through the virtual NIC device (Android 10+)
+
Feedback
Feedback enhancements or bugs to GitHub
Join Telegram Group
@@ -276,6 +280,7 @@
Start Service
Confirm
+
Domain strategy
Routing Settings
Separated by commas(,),choose domain or ip
From 17b7c6d3579f14f435d094fdeb069a7c403a7d7f Mon Sep 17 00:00:00 2001
From: Pk-web6936 <202365630+Pk-web6936@users.noreply.github.com>
Date: Fri, 21 Mar 2025 09:26:35 +0330
Subject: [PATCH 075/238] Update Persian translate (#4395)
* Update Persian translate
Update Persian translate
* Update strings.xml
---
V2rayNG/app/src/main/res/values-fa/strings.xml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/V2rayNG/app/src/main/res/values-fa/strings.xml b/V2rayNG/app/src/main/res/values-fa/strings.xml
index 77815bff..32986dd3 100644
--- a/V2rayNG/app/src/main/res/values-fa/strings.xml
+++ b/V2rayNG/app/src/main/res/values-fa/strings.xml
@@ -27,7 +27,7 @@
حذف کانفیگ
کانفیگ را از QRcode وارد کنید
کانفیگ را از کلیپ بورد وارد کنید
- Import config from locally
+ کانفیگ را از محلی وارد کنید
تایپ دستی[VMESS]
تایپ دستی[VLESS]
تایپ دستی[SHADOWSOCKS]
@@ -136,8 +136,8 @@
در حال دانلود محتوا
خروجی گرفتن در کلیپ بورد
وارد کردن از کلیپ بورد
- Per-app settings
- Enable per-app
+ تنظیمات به تفکیک برنامه
+ فعال کردن به تفکیک برنامه
تنظیمات
From 7a0d997a81058f18d238e151008bb4655b2c981f Mon Sep 17 00:00:00 2001
From: solokot
Date: Fri, 21 Mar 2025 08:57:38 +0300
Subject: [PATCH 076/238] Update Russian translation (#4396)
---
V2rayNG/app/src/main/res/values-ru/strings.xml | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/V2rayNG/app/src/main/res/values-ru/strings.xml b/V2rayNG/app/src/main/res/values-ru/strings.xml
index 05a453fc..ed1b954d 100644
--- a/V2rayNG/app/src/main/res/values-ru/strings.xml
+++ b/V2rayNG/app/src/main/res/values-ru/strings.xml
@@ -26,7 +26,7 @@
Удалить профиль
Импорт из QR-кода
Импорт из буфера обмена
- Import config from locally
+ Импорт из файла
Ручной ввод VMess
Ручной ввод VLESS
Ручной ввод Shadowsocks
@@ -137,16 +137,15 @@
Загрузка данных
Экспорт в буфер обмена
Импорт из буфера обмена
- Per-app settings
- Enable per-app
-
+ Настройки выбранных приложений
+ Использовать выбор приложений
Настройки
Расширенные настройки
Настройки VPN
Прокси для выбранных приложений
- Основной: выделенное приложение соединяется через прокси, не выделенное — напрямую;\nРежим обхода: выделенное приложение соединяется напрямую, не выделенное — через прокси.\nЕсть возможность автоматического выбора проксируемых приложений в меню.
+ Основной: выбранное приложение соединяется через прокси, не выбранное — напрямую;\nРежим обхода: выбранное приложение соединяется напрямую, не выбранное — через прокси.\nЕсть возможность автоматического выбора проксируемых приложений в меню.
Автоподключение при запуске
Автоматически подключаться к выбранному серверу при запуске приложения (может оказаться неудачным)
@@ -303,7 +302,7 @@
Проверить подключение
Проверка…
- Проверка профилей: %d
+ Проверка профилей (%d)
Успешно: HTTP-соединение заняло %d мс
Сбой проверки интернет-соединения: %s
Интернет недоступен
From 4eb5c0263c52aa5e8e13311be39ef0ed99982cc9 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Fri, 21 Mar 2025 16:53:30 +0800
Subject: [PATCH 077/238] Optimize and improve
---
.../com/v2ray/ang/ui/LogcatRecyclerAdapter.kt | 12 +-
.../java/com/v2ray/ang/ui/MainActivity.kt | 206 +++++++++---------
.../v2ray/ang/ui/RoutingSettingActivity.kt | 120 +++++-----
.../res/layout/activity_routing_setting.xml | 5 +-
.../main/res/layout/activity_sub_setting.xml | 30 +--
.../main/res/layout/item_recycler_logcat.xml | 8 +
6 files changed, 190 insertions(+), 191 deletions(-)
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/LogcatRecyclerAdapter.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/LogcatRecyclerAdapter.kt
index 4a9c35fa..c7f92412 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/LogcatRecyclerAdapter.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/LogcatRecyclerAdapter.kt
@@ -11,9 +11,15 @@ class LogcatRecyclerAdapter(val activity: LogcatActivity) : RecyclerView.Adapter
override fun getItemCount() = mActivity.logsets.size
override fun onBindViewHolder(holder: MainViewHolder, position: Int) {
- val content = mActivity.logsets[position]
- holder.itemSubSettingBinding.logContent.text = content
-
+ val log = mActivity.logsets[position]
+ if (log.isEmpty()) {
+ holder.itemSubSettingBinding.logTag.text = ""
+ holder.itemSubSettingBinding.logContent.text = ""
+ } else {
+ val content = log.split("):", limit = 2)
+ holder.itemSubSettingBinding.logTag.text = content.first().trim()
+ holder.itemSubSettingBinding.logContent.text = if (content.count() > 1) content.last().trim() else ""
+ }
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainViewHolder {
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt
index a6ca521d..b8ba755b 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt
@@ -391,24 +391,8 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
// true
// }
- R.id.sub_update -> {
- importConfigViaSub()
- true
- }
-
R.id.export_all -> {
- binding.pbWaiting.show()
- lifecycleScope.launch(Dispatchers.IO) {
- val ret = mainViewModel.exportAllServer()
- launch(Dispatchers.Main) {
- if (ret > 0)
- toast(getString(R.string.title_export_config_count, ret))
- else
- toast(R.string.toast_failure)
- binding.pbWaiting.hide()
- }
- }
-
+ exportAll()
true
}
@@ -430,77 +414,31 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
}
R.id.del_all_config -> {
- AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm)
- .setPositiveButton(android.R.string.ok) { _, _ ->
- binding.pbWaiting.show()
- lifecycleScope.launch(Dispatchers.IO) {
- val ret = mainViewModel.removeAllServer()
- launch(Dispatchers.Main) {
- mainViewModel.reloadServerList()
- toast(getString(R.string.title_del_config_count, ret))
- binding.pbWaiting.hide()
- }
- }
- }
- .setNegativeButton(android.R.string.cancel) { _, _ ->
- //do noting
- }
- .show()
+ delAllConfig()
true
}
R.id.del_duplicate_config -> {
- AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm)
- .setPositiveButton(android.R.string.ok) { _, _ ->
- binding.pbWaiting.show()
- lifecycleScope.launch(Dispatchers.IO) {
- val ret = mainViewModel.removeDuplicateServer()
- launch(Dispatchers.Main) {
- mainViewModel.reloadServerList()
- toast(getString(R.string.title_del_duplicate_config_count, ret))
- binding.pbWaiting.hide()
- }
- }
- }
- .setNegativeButton(android.R.string.cancel) { _, _ ->
- //do noting
- }
- .show()
+ delDuplicateConfig()
true
}
R.id.del_invalid_config -> {
- AlertDialog.Builder(this).setMessage(R.string.del_invalid_config_comfirm)
- .setPositiveButton(android.R.string.ok) { _, _ ->
- binding.pbWaiting.show()
- lifecycleScope.launch(Dispatchers.IO) {
- val ret = mainViewModel.removeInvalidServer()
- launch(Dispatchers.Main) {
- mainViewModel.reloadServerList()
- toast(getString(R.string.title_del_config_count, ret))
- binding.pbWaiting.hide()
- }
- }
- }
- .setNegativeButton(android.R.string.cancel) { _, _ ->
- //do noting
- }
- .show()
+ delInvalidConfig()
true
}
R.id.sort_by_test_results -> {
- binding.pbWaiting.show()
- lifecycleScope.launch(Dispatchers.IO) {
- mainViewModel.sortByTestResults()
- launch(Dispatchers.Main) {
- mainViewModel.reloadServerList()
- binding.pbWaiting.hide()
- }
- }
+ sortByTestResults()
true
}
+ R.id.sub_update -> {
+ importConfigViaSub()
+ true
+ }
+
+
else -> super.onOptionsItemSelected(item)
}
@@ -679,6 +617,88 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
return true
}
+ private fun exportAll() {
+ binding.pbWaiting.show()
+ lifecycleScope.launch(Dispatchers.IO) {
+ val ret = mainViewModel.exportAllServer()
+ launch(Dispatchers.Main) {
+ if (ret > 0)
+ toast(getString(R.string.title_export_config_count, ret))
+ else
+ toast(R.string.toast_failure)
+ binding.pbWaiting.hide()
+ }
+ }
+ }
+
+ private fun delAllConfig() {
+ AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm)
+ .setPositiveButton(android.R.string.ok) { _, _ ->
+ binding.pbWaiting.show()
+ lifecycleScope.launch(Dispatchers.IO) {
+ val ret = mainViewModel.removeAllServer()
+ launch(Dispatchers.Main) {
+ mainViewModel.reloadServerList()
+ toast(getString(R.string.title_del_config_count, ret))
+ binding.pbWaiting.hide()
+ }
+ }
+ }
+ .setNegativeButton(android.R.string.cancel) { _, _ ->
+ //do noting
+ }
+ .show()
+ }
+
+ private fun delDuplicateConfig() {
+ AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm)
+ .setPositiveButton(android.R.string.ok) { _, _ ->
+ binding.pbWaiting.show()
+ lifecycleScope.launch(Dispatchers.IO) {
+ val ret = mainViewModel.removeDuplicateServer()
+ launch(Dispatchers.Main) {
+ mainViewModel.reloadServerList()
+ toast(getString(R.string.title_del_duplicate_config_count, ret))
+ binding.pbWaiting.hide()
+ }
+ }
+ }
+ .setNegativeButton(android.R.string.cancel) { _, _ ->
+ //do noting
+ }
+ .show()
+ }
+
+ private fun delInvalidConfig() {
+ AlertDialog.Builder(this).setMessage(R.string.del_invalid_config_comfirm)
+ .setPositiveButton(android.R.string.ok) { _, _ ->
+ binding.pbWaiting.show()
+ lifecycleScope.launch(Dispatchers.IO) {
+ val ret = mainViewModel.removeInvalidServer()
+ launch(Dispatchers.Main) {
+ mainViewModel.reloadServerList()
+ toast(getString(R.string.title_del_config_count, ret))
+ binding.pbWaiting.hide()
+ }
+ }
+ }
+ .setNegativeButton(android.R.string.cancel) { _, _ ->
+ //do noting
+ }
+ .show()
+ }
+
+ private fun sortByTestResults() {
+ binding.pbWaiting.show()
+ lifecycleScope.launch(Dispatchers.IO) {
+ mainViewModel.sortByTestResults()
+ launch(Dispatchers.Main) {
+ mainViewModel.reloadServerList()
+ binding.pbWaiting.hide()
+ }
+ }
+ }
+
/**
* show file chooser
*/
@@ -772,37 +792,19 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
override fun onNavigationItemSelected(item: MenuItem): Boolean {
// Handle navigation view item clicks here.
when (item.itemId) {
- R.id.sub_setting -> {
- requestSubSettingActivity.launch(Intent(this, SubSettingActivity::class.java))
- }
+ R.id.sub_setting -> requestSubSettingActivity.launch(Intent(this, SubSettingActivity::class.java))
+ R.id.settings -> startActivity(
+ Intent(this, SettingsActivity::class.java)
+ .putExtra("isRunning", mainViewModel.isRunning.value == true)
+ )
- R.id.settings -> {
- startActivity(
- Intent(this, SettingsActivity::class.java)
- .putExtra("isRunning", mainViewModel.isRunning.value == true)
- )
- }
-
- R.id.per_app_proxy_settings -> {
- startActivity(Intent(this, PerAppProxyActivity::class.java))
- }
-
- R.id.routing_setting -> {
- requestSubSettingActivity.launch(Intent(this, RoutingSettingActivity::class.java))
- }
-
- R.id.promotion -> {
- Utils.openUri(this, "${Utils.decode(AppConfig.PromotionUrl)}?t=${System.currentTimeMillis()}")
- }
-
- R.id.logcat -> {
- startActivity(Intent(this, LogcatActivity::class.java))
- }
-
- R.id.about -> {
- startActivity(Intent(this, AboutActivity::class.java))
- }
+ R.id.per_app_proxy_settings -> startActivity(Intent(this, PerAppProxyActivity::class.java))
+ R.id.routing_setting -> requestSubSettingActivity.launch(Intent(this, RoutingSettingActivity::class.java))
+ R.id.promotion -> Utils.openUri(this, "${Utils.decode(AppConfig.PromotionUrl)}?t=${System.currentTimeMillis()}")
+ R.id.logcat -> startActivity(Intent(this, LogcatActivity::class.java))
+ R.id.about -> startActivity(Intent(this, AboutActivity::class.java))
}
+
binding.drawerLayout.closeDrawer(GravityCompat.START)
return true
}
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/RoutingSettingActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/RoutingSettingActivity.kt
index e4fcb0c5..609f0e6c 100644
--- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/RoutingSettingActivity.kt
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/RoutingSettingActivity.kt
@@ -38,7 +38,7 @@ class RoutingSettingActivity : BaseActivity() {
private val preset_rulesets: Array by lazy {
resources.getStringArray(R.array.preset_rulesets)
}
-
+
private val requestCameraPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted: Boolean ->
@@ -85,87 +85,75 @@ class RoutingSettingActivity : BaseActivity() {
return super.onCreateOptionsMenu(menu)
}
- override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
- R.id.add_rule -> {
- startActivity(Intent(this, RoutingEditActivity::class.java))
- true
- }
+ override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) {
+ R.id.add_rule -> startActivity(Intent(this, RoutingEditActivity::class.java)).let { true }
+ R.id.user_asset_setting -> startActivity(Intent(this, UserAssetActivity::class.java)).let { true }
+ R.id.import_predefined_rulesets -> importPredefined().let { true }
+ R.id.import_rulesets_from_clipboard -> importFromClipboard().let { true }
+ R.id.import_rulesets_from_qrcode -> requestCameraPermissionLauncher.launch(Manifest.permission.CAMERA).let { true }
+ R.id.export_rulesets_to_clipboard -> export2Clipboard().let { true }
+ else -> super.onOptionsItemSelected(item)
+ }
- R.id.user_asset_setting -> {
- startActivity(Intent(this, UserAssetActivity::class.java))
- true
- }
-
- R.id.import_predefined_rulesets -> {
+ private fun importPredefined() {
+ AlertDialog.Builder(this).setItems(preset_rulesets.asList().toTypedArray()) { _, i ->
AlertDialog.Builder(this).setMessage(R.string.routing_settings_import_rulesets_tip)
.setPositiveButton(android.R.string.ok) { _, _ ->
- AlertDialog.Builder(this).setItems(preset_rulesets.asList().toTypedArray()) { _, i ->
- try {
- lifecycleScope.launch(Dispatchers.IO) {
- SettingsManager.resetRoutingRulesetsFromPresets(this@RoutingSettingActivity, i)
- launch(Dispatchers.Main) {
- refreshData()
- toast(R.string.toast_success)
- }
- }
- } catch (e: Exception) {
- e.printStackTrace()
- }
- }.show()
- }
- .setNegativeButton(android.R.string.cancel) { _, _ ->
- //do nothing
- }
- .show()
- true
- }
-
- R.id.import_rulesets_from_clipboard -> {
- AlertDialog.Builder(this).setMessage(R.string.routing_settings_import_rulesets_tip)
- .setPositiveButton(android.R.string.ok) { _, _ ->
- val clipboard = try {
- Utils.getClipboard(this)
- } catch (e: Exception) {
- e.printStackTrace()
- toast(R.string.toast_failure)
- return@setPositiveButton
- }
- lifecycleScope.launch(Dispatchers.IO) {
- val result = SettingsManager.resetRoutingRulesets(clipboard)
- withContext(Dispatchers.Main) {
- if (result) {
+ try {
+ lifecycleScope.launch(Dispatchers.IO) {
+ SettingsManager.resetRoutingRulesetsFromPresets(this@RoutingSettingActivity, i)
+ launch(Dispatchers.Main) {
refreshData()
toast(R.string.toast_success)
- } else {
- toast(R.string.toast_failure)
}
}
+ } catch (e: Exception) {
+ e.printStackTrace()
}
}
.setNegativeButton(android.R.string.cancel) { _, _ ->
//do nothing
}
.show()
- true
- }
+ }.show()
+ }
- R.id.import_rulesets_from_qrcode -> {
- requestCameraPermissionLauncher.launch(Manifest.permission.CAMERA)
- true
- }
-
- R.id.export_rulesets_to_clipboard -> {
- val rulesetList = MmkvManager.decodeRoutingRulesets()
- if (rulesetList.isNullOrEmpty()) {
- toast(R.string.toast_failure)
- } else {
- Utils.setClipboard(this, JsonUtil.toJson(rulesetList))
- toast(R.string.toast_success)
+ private fun importFromClipboard() {
+ AlertDialog.Builder(this).setMessage(R.string.routing_settings_import_rulesets_tip)
+ .setPositiveButton(android.R.string.ok) { _, _ ->
+ val clipboard = try {
+ Utils.getClipboard(this)
+ } catch (e: Exception) {
+ e.printStackTrace()
+ toast(R.string.toast_failure)
+ return@setPositiveButton
+ }
+ lifecycleScope.launch(Dispatchers.IO) {
+ val result = SettingsManager.resetRoutingRulesets(clipboard)
+ withContext(Dispatchers.Main) {
+ if (result) {
+ refreshData()
+ toast(R.string.toast_success)
+ } else {
+ toast(R.string.toast_failure)
+ }
+ }
+ }
}
- true
- }
+ .setNegativeButton(android.R.string.cancel) { _, _ ->
+ //do nothing
+ }
+ .show()
+ }
- else -> super.onOptionsItemSelected(item)
+ private fun export2Clipboard() {
+ val rulesetList = MmkvManager.decodeRoutingRulesets()
+ if (rulesetList.isNullOrEmpty()) {
+ toast(R.string.toast_failure)
+ } else {
+ Utils.setClipboard(this, JsonUtil.toJson(rulesetList))
+ toast(R.string.toast_success)
+ }
}
private val scanQRcodeForRulesets = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
diff --git a/V2rayNG/app/src/main/res/layout/activity_routing_setting.xml b/V2rayNG/app/src/main/res/layout/activity_routing_setting.xml
index 17038020..ec98e78b 100644
--- a/V2rayNG/app/src/main/res/layout/activity_routing_setting.xml
+++ b/V2rayNG/app/src/main/res/layout/activity_routing_setting.xml
@@ -7,7 +7,7 @@
android:fitsSystemWindows="true"
tools:context=".ui.RoutingSettingActivity">
-
@@ -55,7 +55,8 @@
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:nestedScrollingEnabled="false"
tools:listitem="@layout/item_recycler_routing_setting" />
-
+
\ No newline at end of file
diff --git a/V2rayNG/app/src/main/res/layout/activity_sub_setting.xml b/V2rayNG/app/src/main/res/layout/activity_sub_setting.xml
index 769cf513..1654701d 100644
--- a/V2rayNG/app/src/main/res/layout/activity_sub_setting.xml
+++ b/V2rayNG/app/src/main/res/layout/activity_sub_setting.xml
@@ -1,31 +1,25 @@
-
-
+ android:layout_height="wrap_content"
+ android:indeterminate="true"
+ android:visibility="invisible"
+ app:indicatorColor="@color/color_fab_active" />
-
+
-
-
\ No newline at end of file
diff --git a/V2rayNG/app/src/main/res/layout/item_recycler_logcat.xml b/V2rayNG/app/src/main/res/layout/item_recycler_logcat.xml
index a6cfadec..f71e28b9 100644
--- a/V2rayNG/app/src/main/res/layout/item_recycler_logcat.xml
+++ b/V2rayNG/app/src/main/res/layout/item_recycler_logcat.xml
@@ -13,11 +13,19 @@
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin_spacing_dp8"
android:paddingTop="@dimen/margin_spacing_dp8"
+ android:orientation="vertical"
android:paddingBottom="@dimen/margin_spacing_dp8">
+
+
From f0de5275b99d84c0ab3e32d7fa78ea9dc9e1a58e Mon Sep 17 00:00:00 2001
From: Pk-web6936 <202365630+Pk-web6936@users.noreply.github.com>
Date: Fri, 21 Mar 2025 13:40:10 +0330
Subject: [PATCH 078/238] Update kotlin version to 2.1.20 (#4397)
* Update kotlin version to 2.1.20
Update kotlin version to 2.1.20
* Update kotlin version to 2.1.20
Update kotlin version to 2.1.20
* Update gradle-wrapper.properties
---
README.md | 2 +-
V2rayNG/gradle/libs.versions.toml | 2 +-
V2rayNG/gradle/wrapper/gradle-wrapper.properties | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/README.md b/README.md
index c23ee0cf..a8e22504 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
A V2Ray client for Android, support [Xray core](https://github.com/XTLS/Xray-core) and [v2fly core](https://github.com/v2fly/v2ray-core)
[](https://developer.android.com/about/versions/lollipop)
-[](https://kotlinlang.org)
+[](https://kotlinlang.org)
[](https://github.com/2dust/v2rayNG/commits/master)
[](https://www.codefactor.io/repository/github/2dust/v2rayng)
[](https://github.com/2dust/v2rayNG/releases)
diff --git a/V2rayNG/gradle/libs.versions.toml b/V2rayNG/gradle/libs.versions.toml
index 6cc79019..1c816657 100644
--- a/V2rayNG/gradle/libs.versions.toml
+++ b/V2rayNG/gradle/libs.versions.toml
@@ -2,7 +2,7 @@
agp = "8.9.0"
desugar_jdk_libs = "2.1.5"
gradleLicensePlugin = "0.9.8"
-kotlin = "2.1.10"
+kotlin = "2.1.20"
coreKtx = "1.15.0"
junit = "4.13.2"
junitVersion = "1.2.1"
diff --git a/V2rayNG/gradle/wrapper/gradle-wrapper.properties b/V2rayNG/gradle/wrapper/gradle-wrapper.properties
index 13e057c9..f221584f 100644
--- a/V2rayNG/gradle/wrapper/gradle-wrapper.properties
+++ b/V2rayNG/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
#Thu Nov 14 12:42:51 BDT 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
From eef6e60dbb1fd645307b840585d7a37e2d58a41b Mon Sep 17 00:00:00 2001
From: hhhkkmk <201595863+hhhkkmk@users.noreply.github.com>
Date: Fri, 21 Mar 2025 18:13:04 +0800
Subject: [PATCH 079/238] optimization (#4393)
* optimization
* fix
* fix2
* fix3
---
.github/workflows/build.yml | 59 +++++++++++++++----------------------
compile-tun2socks.sh | 7 ++---
libhysteria2.sh | 2 +-
tun2socks.mk | 2 +-
4 files changed, 29 insertions(+), 41 deletions(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 66dabd71..f4173ad5 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -21,47 +21,41 @@ jobs:
submodules: 'recursive'
fetch-depth: '0'
+ - name: Setup Android SDK
+ uses: android-actions/setup-android@v3
+ with:
+ log-accepted-android-sdk-licenses: false
+ cmdline-tools-version: '12266719'
+ packages: 'platforms;android-35 build-tools;35.0.0 platform-tools'
+
+ - name: Install NDK
+ run: |
+ echo "y" | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager \
+ --channel=3 \
+ --install "ndk;29.0.13113456"
+ echo "NDK_HOME=$ANDROID_HOME/ndk/29.0.13113456" >> $GITHUB_ENV
+ sed -i '10i\
+ \
+ ndkVersion = "29.0.13113456"' ${{ github.workspace }}/V2rayNG/app/build.gradle.kts
+
- name: Restore cached libtun2socks
id: cache-libtun2socks-restore
uses: actions/cache/restore@v4
with:
path: ${{ github.workspace }}/libs
- key: libtun2socks-${{ runner.os }}-${{ hashFiles('.git/modules/badvpn/HEAD') }}-${{ hashFiles('.git/modules/libancillary/HEAD') }}
-
- - name: Setup Android NDK
- uses: nttld/setup-ndk@v1
- id: setup-ndk
- # Same version as https://gitlab.com/fdroid/fdroiddata/metadata/com.v2ray.ang.yml
- with:
- ndk-version: r27
- add-to-path: true
- link-to-sdk: true
- local-cache: true
-
- - name: Restore Android Symlinks
- run: |
- directory="${{ steps.setup-ndk.outputs.ndk-path }}/toolchains/llvm/prebuilt/linux-x86_64/bin"
- find "$directory" -type l | while read link; do
- current_target=$(readlink "$link")
- new_target="$directory/$(basename "$current_target")"
- ln -sf "$new_target" "$link"
- echo "Changed $(basename "$link") from $current_target to $new_target"
- done
+ key: libtun2socks-${{ runner.os }}-${{ env.NDK_HOME }}-${{ hashFiles('.git/modules/badvpn/HEAD') }}-${{ hashFiles('.git/modules/libancillary/HEAD') }}
- name: Build libtun2socks
if: steps.cache-libtun2socks-restore.outputs.cache-hit != 'true'
run: |
bash compile-tun2socks.sh
- tar -xvzf libtun2socks.so.tgz
- env:
- NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
- name: Save libtun2socks
if: steps.cache-libtun2socks-restore.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
path: ${{ github.workspace }}/libs
- key: libtun2socks-${{ runner.os }}-${{ hashFiles('.git/modules/badvpn/HEAD') }}-${{ hashFiles('.git/modules/libancillary/HEAD') }}
+ key: libtun2socks-${{ runner.os }}-${{ env.NDK_HOME }}-${{ hashFiles('.git/modules/badvpn/HEAD') }}-${{ hashFiles('.git/modules/libancillary/HEAD') }}
- name: Copy libtun2socks
run: |
@@ -88,27 +82,26 @@ jobs:
uses: actions/cache/restore@v4
with:
path: ${{ github.workspace }}/hysteria/libs
- key: libhysteria2-${{ runner.os }}-${{ hashFiles('.git/modules/hysteria/HEAD') }}-${{ hashFiles('libhysteria2.sh') }}
+ key: libhysteria2-${{ runner.os }}-${{ env.NDK_HOME }}-${{ hashFiles('.git/modules/hysteria/HEAD') }}-${{ hashFiles('libhysteria2.sh') }}
- name: Setup Golang
if: steps.cache-libhysteria2-restore.outputs.cache-hit != 'true'
uses: actions/setup-go@v5
with:
go-version-file: 'AndroidLibXrayLite/go.mod'
+ cache: false
- name: Build libhysteria2
if: steps.cache-libhysteria2-restore.outputs.cache-hit != 'true'
run: |
bash libhysteria2.sh
- env:
- ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
- name: Save libhysteria2
if: steps.cache-libhysteria2-restore.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
path: ${{ github.workspace }}/hysteria/libs
- key: libhysteria2-${{ runner.os }}-${{ hashFiles('.git/modules/hysteria/HEAD') }}-${{ hashFiles('libhysteria2.sh') }}
+ key: libhysteria2-${{ runner.os }}-${{ env.NDK_HOME }}-${{ hashFiles('.git/modules/hysteria/HEAD') }}-${{ hashFiles('libhysteria2.sh') }}
- name: Copy libhysteria2
run: |
@@ -120,9 +113,6 @@ jobs:
distribution: 'temurin'
java-version: '21'
- - name: Setup Android environment
- uses: android-actions/setup-android@v3
-
- name: Decode Keystore
uses: timheuer/base64-to-file@v1
id: android_keystore
@@ -133,12 +123,11 @@ jobs:
- name: Build APK
run: |
cd ${{ github.workspace }}/V2rayNG
+ echo "sdk.dir=${ANDROID_HOME}" > local.properties
chmod 755 gradlew
./gradlew licenseFdroidReleaseReport
./gradlew assembleRelease -Pandroid.injected.signing.store.file=${{ steps.android_keystore.outputs.filePath }} -Pandroid.injected.signing.store.password=${{ secrets.APP_KEYSTORE_PASSWORD }} -Pandroid.injected.signing.key.alias=${{ secrets.APP_KEYSTORE_ALIAS }} -Pandroid.injected.signing.key.password=${{ secrets.APP_KEY_PASSWORD }}
- env:
- ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
-
+
- name: Upload arm64-v8a APK
uses: actions/upload-artifact@v4
if: ${{ success() }}
diff --git a/compile-tun2socks.sh b/compile-tun2socks.sh
index 11bea0d2..c0db6218 100644
--- a/compile-tun2socks.sh
+++ b/compile-tun2socks.sh
@@ -23,11 +23,10 @@ $NDK_HOME/ndk-build \
NDK_PROJECT_PATH=. \
APP_BUILD_SCRIPT=./tun2socks.mk \
APP_ABI=all \
- APP_PLATFORM=android-19 \
+ APP_PLATFORM=android-21 \
NDK_LIBS_OUT=$TMPDIR/libs \
NDK_OUT=$TMPDIR/tmp \
- APP_SHORT_COMMANDS=false LOCAL_SHORT_COMMANDS=false -B -j4 \
- LOCAL_LDFLAGS=-Wl,--build-id=none
-tar cvfz $__dir/libtun2socks.so.tgz libs
+ APP_SHORT_COMMANDS=false LOCAL_SHORT_COMMANDS=false -B -j4
+cp -r $TMPDIR/libs $__dir/
popd
rm -rf $TMPDIR
diff --git a/libhysteria2.sh b/libhysteria2.sh
index b2a72117..1bd88967 100644
--- a/libhysteria2.sh
+++ b/libhysteria2.sh
@@ -14,7 +14,7 @@ for target in "${targets[@]}"; do
echo "Building for ${abi} with ${ndk_target} (${goarch})"
- CC="${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/${ndk_target}-clang" CGO_ENABLED=1 CGO_LDFLAGS="-Wl,-z,max-page-size=16384" GOOS=android GOARCH=$goarch go build -o libs/$abi/libhysteria2.so -trimpath -ldflags "-s -w -buildid=" -buildvcs=false ./app
+ CC="${NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/${ndk_target}-clang" CGO_ENABLED=1 GOOS=android GOARCH=$goarch go build -o libs/$abi/libhysteria2.so -trimpath -ldflags "-s -w -buildid=" -buildvcs=false ./app
echo "Built libhysteria2.so for ${abi}"
done
diff --git a/tun2socks.mk b/tun2socks.mk
index 04ac8df3..1fab134e 100644
--- a/tun2socks.mk
+++ b/tun2socks.mk
@@ -37,7 +37,6 @@ LOCAL_CFLAGS += -DNDEBUG -DANDROID
LOCAL_CFLAGS += -I
LOCAL_STATIC_LIBRARIES := libancillary
LOCAL_C_INCLUDES := \
- $(LOCAL_PATH)/badvpn/libancillary \
$(LOCAL_PATH)/badvpn/lwip/src/include/ipv4 \
$(LOCAL_PATH)/badvpn/lwip/src/include/ipv6 \
$(LOCAL_PATH)/badvpn/lwip/src/include \
@@ -110,6 +109,7 @@ TUN2SOCKS_SOURCES := \
socks_udp_client/SocksUdpClient.c
LOCAL_MODULE := tun2socks
LOCAL_LDLIBS := -ldl -llog
+LOCAL_LDFLAGS=-Wl,--build-id=none
LOCAL_SRC_FILES := $(addprefix badvpn/, $(TUN2SOCKS_SOURCES))
LOCAL_BUILD_SCRIPT := BUILD_EXECUTABLE
LOCAL_MAKEFILE := $(local-makefile)
From fd9f912c187fffc7df2ac4c732294288ecd19568 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Sat, 22 Mar 2025 17:50:55 +0800
Subject: [PATCH 080/238] Optimize and improve theme
---
V2rayNG/app/src/main/res/layout/activity_about.xml | 3 +--
V2rayNG/app/src/main/res/layout/activity_bypass_list.xml | 1 -
V2rayNG/app/src/main/res/layout/activity_logcat.xml | 1 -
V2rayNG/app/src/main/res/layout/activity_routing_edit.xml | 1 -
.../app/src/main/res/layout/activity_routing_setting.xml | 1 -
.../src/main/res/layout/activity_server_custom_config.xml | 1 -
.../app/src/main/res/layout/activity_server_hysteria2.xml | 1 -
.../src/main/res/layout/activity_server_shadowsocks.xml | 1 -
V2rayNG/app/src/main/res/layout/activity_server_socks.xml | 1 -
.../app/src/main/res/layout/activity_server_trojan.xml | 1 -
V2rayNG/app/src/main/res/layout/activity_server_vless.xml | 1 -
V2rayNG/app/src/main/res/layout/activity_server_vmess.xml | 1 -
.../app/src/main/res/layout/activity_server_wireguard.xml | 1 -
V2rayNG/app/src/main/res/layout/activity_settings.xml | 1 -
V2rayNG/app/src/main/res/layout/activity_sub_edit.xml | 1 -
V2rayNG/app/src/main/res/layout/activity_sub_setting.xml | 1 -
V2rayNG/app/src/main/res/layout/activity_tasker.xml | 1 -
.../app/src/main/res/layout/activity_user_asset_url.xml | 1 -
.../app/src/main/res/layout/item_recycler_user_asset.xml | 1 -
V2rayNG/app/src/main/res/layout/widget_switch.xml | 2 +-
V2rayNG/app/src/main/res/menu/menu_drawer.xml | 8 ++++----
V2rayNG/app/src/main/res/values-night/themes.xml | 3 ++-
V2rayNG/app/src/main/res/values/themes.xml | 3 ++-
23 files changed, 10 insertions(+), 27 deletions(-)
diff --git a/V2rayNG/app/src/main/res/layout/activity_about.xml b/V2rayNG/app/src/main/res/layout/activity_about.xml
index 4651bafe..7479371b 100644
--- a/V2rayNG/app/src/main/res/layout/activity_about.xml
+++ b/V2rayNG/app/src/main/res/layout/activity_about.xml
@@ -1,8 +1,7 @@
+ android:layout_height="match_parent">
diff --git a/V2rayNG/app/src/main/res/layout/activity_sub_edit.xml b/V2rayNG/app/src/main/res/layout/activity_sub_edit.xml
index aecaf02e..d30f6e11 100644
--- a/V2rayNG/app/src/main/res/layout/activity_sub_edit.xml
+++ b/V2rayNG/app/src/main/res/layout/activity_sub_edit.xml
@@ -4,7 +4,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
- android:fitsSystemWindows="true"
tools:context=".ui.SubSettingActivity">
diff --git a/V2rayNG/app/src/main/res/layout/activity_user_asset_url.xml b/V2rayNG/app/src/main/res/layout/activity_user_asset_url.xml
index 060b03be..b2ac45c5 100644
--- a/V2rayNG/app/src/main/res/layout/activity_user_asset_url.xml
+++ b/V2rayNG/app/src/main/res/layout/activity_user_asset_url.xml
@@ -3,7 +3,6 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:fitsSystemWindows="true"
tools:context=".ui.UserAssetUrlActivity">
diff --git a/V2rayNG/app/src/main/res/layout/widget_switch.xml b/V2rayNG/app/src/main/res/layout/widget_switch.xml
index 50776f87..11312d5f 100644
--- a/V2rayNG/app/src/main/res/layout/widget_switch.xml
+++ b/V2rayNG/app/src/main/res/layout/widget_switch.xml
@@ -27,5 +27,5 @@
android:layout_height="wrap_content"
android:text="@string/app_name"
android:textColor="@android:color/white"
- android:textSize="10sp" />
+ android:textAppearance="@style/TextAppearance.AppCompat.Small" />
diff --git a/V2rayNG/app/src/main/res/menu/menu_drawer.xml b/V2rayNG/app/src/main/res/menu/menu_drawer.xml
index a2b17dd1..1e244034 100644
--- a/V2rayNG/app/src/main/res/menu/menu_drawer.xml
+++ b/V2rayNG/app/src/main/res/menu/menu_drawer.xml
@@ -8,10 +8,6 @@
android:id="@+id/sub_setting"
android:icon="@drawable/ic_subscriptions_24dp"
android:title="@string/title_sub_setting" />
-
+
diff --git a/V2rayNG/app/src/main/res/values-night/themes.xml b/V2rayNG/app/src/main/res/values-night/themes.xml
index 828ffe7c..f25399fa 100644
--- a/V2rayNG/app/src/main/res/values-night/themes.xml
+++ b/V2rayNG/app/src/main/res/values-night/themes.xml
@@ -5,8 +5,9 @@
- @color/colorPrimary
- @color/colorAccent
- @color/colorPrimary
- - @color/colorPrimary
+ - @android:color/transparent
- @color/colorAccent
+ - true
\ No newline at end of file
diff --git a/V2rayNG/app/src/main/res/values/themes.xml b/V2rayNG/app/src/main/res/values/themes.xml
index d6558e07..1703fd0f 100644
--- a/V2rayNG/app/src/main/res/values/themes.xml
+++ b/V2rayNG/app/src/main/res/values/themes.xml
@@ -4,8 +4,9 @@
- @color/colorPrimary
- @color/colorAccent
- @color/colorPrimary
- - @color/colorPrimary
+ - @android:color/transparent
- @color/colorAccent
+ - true
\ No newline at end of file
diff --git a/V2rayNG/app/src/main/res/values/themes.xml b/V2rayNG/app/src/main/res/values/themes.xml
index f03eed7b..6ad204c0 100644
--- a/V2rayNG/app/src/main/res/values/themes.xml
+++ b/V2rayNG/app/src/main/res/values/themes.xml
@@ -7,7 +7,6 @@
- @color/colorPrimary
- @android:color/transparent
- @color/colorAccent
- - true