From 2d32653f5b388ff6f23156f6388f5fbbd060716e Mon Sep 17 00:00:00 2001
From: armv9 <48624112+arm64v8a@users.noreply.github.com>
Date: Wed, 15 Oct 2025 10:01:25 +0900
Subject: [PATCH 01/18] update core
---
buildScript/lib/core/get_source_env.sh | 2 +-
libcore/go.mod | 2 +-
libcore/go.sum | 4 ++--
nb4a.properties | 2 +-
4 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/buildScript/lib/core/get_source_env.sh b/buildScript/lib/core/get_source_env.sh
index 886a8f05d..2c4bbef03 100644
--- a/buildScript/lib/core/get_source_env.sh
+++ b/buildScript/lib/core/get_source_env.sh
@@ -1,2 +1,2 @@
-export COMMIT_SING_BOX="b250e570fe32ff8fd2e34f29afb8e87f83d3cab3"
+export COMMIT_SING_BOX="3330efd23adeb2d7453da4e99c2fd4f06c76e9e7"
export COMMIT_LIBNEKO="1c47a3af71990a7b2192e03292b4d246c308ef0b"
diff --git a/libcore/go.mod b/libcore/go.mod
index a46e0e1d1..a314c56d9 100644
--- a/libcore/go.mod
+++ b/libcore/go.mod
@@ -43,7 +43,7 @@ require (
github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42 // indirect
github.com/mdlayher/socket v0.5.1 // indirect
github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4 // indirect
- github.com/metacubex/utls v1.8.0 // indirect
+ github.com/metacubex/utls v1.8.2 // indirect
github.com/mholt/acmez/v3 v3.1.2 // indirect
github.com/quic-go/qpack v0.5.1 // indirect
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a // indirect
diff --git a/libcore/go.sum b/libcore/go.sum
index aeb360f85..593788223 100644
--- a/libcore/go.sum
+++ b/libcore/go.sum
@@ -61,8 +61,8 @@ github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4 h1:j1VRTiC9JLR4nUbSikx9OGdu/3AgFDqgcLj4GoqyQkc=
github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw=
-github.com/metacubex/utls v1.8.0 h1:mSYi6FMnmc5riARl5UZDmWVy710z+P5b7xuGW0lV9ac=
-github.com/metacubex/utls v1.8.0/go.mod h1:FdjYzVfCtgtna19hX0ER1Xsa5uJInwdQ4IcaaI98lEQ=
+github.com/metacubex/utls v1.8.2 h1:d7KalMZ5hnOJ6lThMz8Ykd+5dvmXH3Eoeyfv2jUuG3w=
+github.com/metacubex/utls v1.8.2/go.mod h1:kncGGVhFaoGn5M3pFe3SXhZCzsbCJayNOH4UEqTKTko=
github.com/mholt/acmez/v3 v3.1.2 h1:auob8J/0FhmdClQicvJvuDavgd5ezwLBfKuYmynhYzc=
github.com/mholt/acmez/v3 v3.1.2/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
github.com/miekg/dns v1.1.67 h1:kg0EHj0G4bfT5/oOys6HhZw4vmMlnoZ+gDu8tJ/AlI0=
diff --git a/nb4a.properties b/nb4a.properties
index 3fda6cf07..09d606d8d 100644
--- a/nb4a.properties
+++ b/nb4a.properties
@@ -1,4 +1,4 @@
PACKAGE_NAME=moe.nb4a
VERSION_NAME=1.4.0
-PRE_VERSION_NAME=pre-1.4.1-20251007-1
+PRE_VERSION_NAME=pre-1.4.1-20251015-1
VERSION_CODE=44
From 596b9ebfbb5394b5b6092a7c07cb7202f1fe511b Mon Sep 17 00:00:00 2001
From: xymopen_Official <8053733+xymopen@users.noreply.github.com>
Date: Tue, 21 Oct 2025 20:31:41 +0800
Subject: [PATCH 02/18] fix: Certain single-label domain names can only be
resolved by `InetAddress.getAllByName()` but not `Network.getAllByName()`
(#1050)
---
.../java/moe/matsuri/nb4a/net/LocalResolverImpl.kt | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/app/src/main/java/moe/matsuri/nb4a/net/LocalResolverImpl.kt b/app/src/main/java/moe/matsuri/nb4a/net/LocalResolverImpl.kt
index 7de35b44e..9b125aeea 100644
--- a/app/src/main/java/moe/matsuri/nb4a/net/LocalResolverImpl.kt
+++ b/app/src/main/java/moe/matsuri/nb4a/net/LocalResolverImpl.kt
@@ -128,11 +128,11 @@ object LocalResolverImpl : LocalDNSTransport {
// 老版本系统,继续用阻塞的 InetAddress
try {
val u = SagerNet.underlyingNetwork
- val answer = if (u != null) {
- u.getAllByName(domain)
- } else {
- InetAddress.getAllByName(domain)
- }
+ val answer = try {
+ u?.getAllByName(domain)
+ } catch (e: UnknownHostException) {
+ null
+ } ?: InetAddress.getAllByName(domain)
if (answer != null) {
ctx.success(answer.mapNotNull { it.hostAddress }.joinToString("\n"))
} else {
From 110f3b21b2e77b1ad45abf21c9fa3d8f1beff30a Mon Sep 17 00:00:00 2001
From: mak7im01 <99025360+mak7im01@users.noreply.github.com>
Date: Tue, 21 Oct 2025 15:32:11 +0300
Subject: [PATCH 03/18] Update Russian translation (#1052)
Added new lines with translation
---
app/src/main/res/values-ru/strings.xml | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 8d47da455..274652f20 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -75,6 +75,9 @@
Настройки цепочки
Введено символов: %1$d из %2$d
Превышено ограничение на количество символов (%1$d из %2$d)
+Обновления не найдены.
+Проверить наличие обновлений предварительной версии
+Проверить наличие обновлений релизнойной версии
Циклическая ссылка
Маршрут не может содержать сам себя.
Очистить журнал
@@ -250,6 +253,7 @@
Трафик
Подсказка о соединении с лимитным тарифным планом
Подсказывать системе, что VPN следует рассматривать как сеть с лимитным тарифным планом
+Свернуть
Отсутствующий плагин
Переместить
Новое уведомление
@@ -319,6 +323,7 @@
Транспрокси-порт
Предпочитать
Текст "%1$s" скопирован в буфер обмена
+Это приложение является предварительной версией и может содержать множество проблем. Если вы не хотите тестировать его, скачайте релизную версию с GitHub!
Конфигурация профиля
Пожалуйста, выберите профиль
Импортировать профиль
@@ -342,6 +347,8 @@
Удаленный DNS
Удалить дубликаты серверов
Сбросить соединения
+Восстановить настройки по умолчанию
+Восстановить настройки по умолчанию, такие данные, как узлы и группы, будут сохранены. Для полной очистки данных, очистите данные приложения непосредственно в системных настройках.
Определить адрес назначения
Если адрес назначения является доменом, то он передается в соответствии с правилом IPv6.
Создать маршрут
@@ -453,6 +460,8 @@
Есть несохраненные изменения. Сохранить?
Обновить все подписки
Обновить подписку текущей группы
+Текущая версия: %1$s\nДоступная версия: %2$s\nХотите загрузить её?
+Доступна новая версия
Обновить настройки
Прокси-сервер не подключен, вы уверены, что хотите продолжить обновление?
Обновлять только при подключении
From 4326aab16d53635326ac5bc5065f8eb30278c1b9 Mon Sep 17 00:00:00 2001
From: starifly <2003879+starifly@users.noreply.github.com>
Date: Mon, 20 Oct 2025 22:14:56 +0800
Subject: [PATCH 04/18] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20grpc=20case?=
=?UTF-8?q?=20=E7=BC=BA=E5=B0=91=20break=20=E5=AF=BC=E8=87=B4=E5=BA=8F?=
=?UTF-8?q?=E5=88=97=E5=8C=96=E6=95=B0=E6=8D=AE=E9=94=99=E4=B9=B1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../io/nekohasekai/sagernet/fmt/v2ray/StandardV2RayBean.java | 2 ++
1 file changed, 2 insertions(+)
diff --git a/app/src/main/java/io/nekohasekai/sagernet/fmt/v2ray/StandardV2RayBean.java b/app/src/main/java/io/nekohasekai/sagernet/fmt/v2ray/StandardV2RayBean.java
index 61a2a7323..b30925712 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/fmt/v2ray/StandardV2RayBean.java
+++ b/app/src/main/java/io/nekohasekai/sagernet/fmt/v2ray/StandardV2RayBean.java
@@ -140,6 +140,7 @@ public void serialize(ByteBufferOutput output) {
}
case "grpc": {
output.writeString(path);
+ break;
}
case "httpupgrade": {
output.writeString(host);
@@ -200,6 +201,7 @@ public void deserialize(ByteBufferInput input) {
}
case "grpc": {
path = input.readString();
+ break;
}
case "httpupgrade": {
host = input.readString();
From aa275d5e100c909ae4e815ab168f86f6e06c24ef Mon Sep 17 00:00:00 2001
From: armv9 <48624112+arm64v8a@users.noreply.github.com>
Date: Tue, 21 Oct 2025 21:42:44 +0900
Subject: [PATCH 05/18] update preview version
---
app/src/main/res/values-fa/strings.xml | 4 ----
app/src/main/res/values-ru/strings.xml | 2 +-
libcore/go.mod | 2 +-
libcore/go.sum | 4 ++--
nb4a.properties | 2 +-
5 files changed, 5 insertions(+), 9 deletions(-)
diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml
index bb9291cb2..de972072d 100644
--- a/app/src/main/res/values-fa/strings.xml
+++ b/app/src/main/res/values-fa/strings.xml
@@ -208,7 +208,6 @@
Trojan Go
Mieru
Naïve
- Ping Tunnel
Hysteria
SSH
WireGuard
@@ -521,10 +520,7 @@
فعال کردن ECH
فعال کردن ECH
تنظیمات ECH
- فعال کردن پشتیبانی از امضای مجوز post-quantum همتا
- اندازه تطبیقی رکوردهای TLS را غیرفعال میکند
پیکربندی ECH
- اگر فعال باشد، همیشه از بزرگترین اندازه رکورد TLS ممکن استفاده میشود. در صورت غیرفعال کردن، اندازه رکوردهای TLS ممکن است در تلاش برای بهبود تاخیر تنظیم شود.
میزبان HTTPUpgrade
مسیر HTTPUpgrade
بهروزرسانی اشتراک این گروه
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 274652f20..c45db7492 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -460,7 +460,7 @@
Есть несохраненные изменения. Сохранить?
Обновить все подписки
Обновить подписку текущей группы
-Текущая версия: %1$s\nДоступная версия: %2$s\nХотите загрузить её?
+Текущая версия: %1$s\nДоступная версия: %2$s\nХотите загрузить её?
Доступна новая версия
Обновить настройки
Прокси-сервер не подключен, вы уверены, что хотите продолжить обновление?
diff --git a/libcore/go.mod b/libcore/go.mod
index a314c56d9..0afc344d6 100644
--- a/libcore/go.mod
+++ b/libcore/go.mod
@@ -12,7 +12,7 @@ require (
github.com/sagernet/sing v0.7.12
github.com/sagernet/sing-box v1.0.0 // replaced
github.com/sagernet/sing-tun v0.7.2
- github.com/ulikunitz/xz v0.5.11
+ github.com/ulikunitz/xz v0.5.15
golang.org/x/mobile v0.0.0-20231108233038-35478a0c49da
golang.org/x/sys v0.35.0
)
diff --git a/libcore/go.sum b/libcore/go.sum
index 593788223..acf71ab3b 100644
--- a/libcore/go.sum
+++ b/libcore/go.sum
@@ -122,8 +122,8 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
-github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8=
-github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
+github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY=
+github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY=
diff --git a/nb4a.properties b/nb4a.properties
index 09d606d8d..0701e3f13 100644
--- a/nb4a.properties
+++ b/nb4a.properties
@@ -1,4 +1,4 @@
PACKAGE_NAME=moe.nb4a
VERSION_NAME=1.4.0
-PRE_VERSION_NAME=pre-1.4.1-20251015-1
+PRE_VERSION_NAME=pre-1.4.1-20251021-1
VERSION_CODE=44
From b39ac9a8f0eaa35978aad3d31e94aea2fd5c2420 Mon Sep 17 00:00:00 2001
From: armv9 <48624112+arm64v8a@users.noreply.github.com>
Date: Tue, 21 Oct 2025 21:58:37 +0900
Subject: [PATCH 06/18] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E8=80=81?=
=?UTF-8?q?=E7=89=88=E6=9C=AC=20grpc=20=E6=95=B0=E6=8D=AE=E8=AF=BB?=
=?UTF-8?q?=E5=8F=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../sagernet/fmt/v2ray/StandardV2RayBean.java | 22 +++++++++----------
1 file changed, 10 insertions(+), 12 deletions(-)
diff --git a/app/src/main/java/io/nekohasekai/sagernet/fmt/v2ray/StandardV2RayBean.java b/app/src/main/java/io/nekohasekai/sagernet/fmt/v2ray/StandardV2RayBean.java
index b30925712..2f32c50f4 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/fmt/v2ray/StandardV2RayBean.java
+++ b/app/src/main/java/io/nekohasekai/sagernet/fmt/v2ray/StandardV2RayBean.java
@@ -112,7 +112,7 @@ public void initializeDefaultValues() {
@Override
public void serialize(ByteBufferOutput output) {
- output.writeInt(3);
+ output.writeInt(4);
super.serialize(output);
output.writeString(uuid);
output.writeString(encryption);
@@ -133,7 +133,8 @@ public void serialize(ByteBufferOutput output) {
output.writeString(earlyDataHeaderName);
break;
}
- case "http": {
+ case "http":
+ case "httpupgrade": {
output.writeString(host);
output.writeString(path);
break;
@@ -142,11 +143,6 @@ public void serialize(ByteBufferOutput output) {
output.writeString(path);
break;
}
- case "httpupgrade": {
- output.writeString(host);
- output.writeString(path);
-
- }
}
output.writeString(security);
@@ -194,19 +190,21 @@ public void deserialize(ByteBufferInput input) {
earlyDataHeaderName = input.readString();
break;
}
- case "http": {
+ case "http":
+ case "httpupgrade": {
host = input.readString();
path = input.readString();
break;
}
case "grpc": {
path = input.readString();
+ if (version < 4) {
+ // 解决老版本数据的读取问题
+ input.readString();
+ input.readString();
+ }
break;
}
- case "httpupgrade": {
- host = input.readString();
- path = input.readString();
- }
}
security = input.readString();
From e6071e35f235debca1cdd72518d58a467800b226 Mon Sep 17 00:00:00 2001
From: armv9 <48624112+arm64v8a@users.noreply.github.com>
Date: Thu, 23 Oct 2025 11:23:09 +0900
Subject: [PATCH 07/18] update core
---
buildScript/lib/core/get_source_env.sh | 2 +-
libcore/go.mod | 6 +++---
libcore/go.sum | 12 ++++++------
3 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/buildScript/lib/core/get_source_env.sh b/buildScript/lib/core/get_source_env.sh
index 2c4bbef03..5c7bc0be9 100644
--- a/buildScript/lib/core/get_source_env.sh
+++ b/buildScript/lib/core/get_source_env.sh
@@ -1,2 +1,2 @@
-export COMMIT_SING_BOX="3330efd23adeb2d7453da4e99c2fd4f06c76e9e7"
+export COMMIT_SING_BOX="98c74d272a6b61abc120ff8163ab2ead0bb0ce96"
export COMMIT_LIBNEKO="1c47a3af71990a7b2192e03292b4d246c308ef0b"
diff --git a/libcore/go.mod b/libcore/go.mod
index 0afc344d6..daaf48eee 100644
--- a/libcore/go.mod
+++ b/libcore/go.mod
@@ -11,7 +11,7 @@ require (
github.com/sagernet/quic-go v0.52.0-sing-box-mod.2
github.com/sagernet/sing v0.7.12
github.com/sagernet/sing-box v1.0.0 // replaced
- github.com/sagernet/sing-tun v0.7.2
+ github.com/sagernet/sing-tun v0.7.3
github.com/ulikunitz/xz v0.5.15
golang.org/x/mobile v0.0.0-20231108233038-35478a0c49da
golang.org/x/sys v0.35.0
@@ -42,8 +42,8 @@ require (
github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42 // indirect
github.com/mdlayher/socket v0.5.1 // indirect
- github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4 // indirect
- github.com/metacubex/utls v1.8.2 // indirect
+ github.com/metacubex/tfo-go v0.0.0-20250921095601-b102db4216c0 // indirect
+ github.com/metacubex/utls v1.8.3 // indirect
github.com/mholt/acmez/v3 v3.1.2 // indirect
github.com/quic-go/qpack v0.5.1 // indirect
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a // indirect
diff --git a/libcore/go.sum b/libcore/go.sum
index acf71ab3b..b516d865b 100644
--- a/libcore/go.sum
+++ b/libcore/go.sum
@@ -59,10 +59,10 @@ github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42 h1:A1Cq6Ysb0GM0
github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42/go.mod h1:BB4YCPDOzfy7FniQ/lxuYQ3dgmM2cZumHbK8RpTjN2o=
github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos=
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
-github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4 h1:j1VRTiC9JLR4nUbSikx9OGdu/3AgFDqgcLj4GoqyQkc=
-github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw=
-github.com/metacubex/utls v1.8.2 h1:d7KalMZ5hnOJ6lThMz8Ykd+5dvmXH3Eoeyfv2jUuG3w=
-github.com/metacubex/utls v1.8.2/go.mod h1:kncGGVhFaoGn5M3pFe3SXhZCzsbCJayNOH4UEqTKTko=
+github.com/metacubex/tfo-go v0.0.0-20250921095601-b102db4216c0 h1:Ui+/2s5Qz0lSnDUBmEL12M5Oi/PzvFxGTNohm8ZcsmE=
+github.com/metacubex/tfo-go v0.0.0-20250921095601-b102db4216c0/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw=
+github.com/metacubex/utls v1.8.3 h1:0m/yCxm3SK6kWve2lKiFb1pue1wHitJ8sQQD4Ikqde4=
+github.com/metacubex/utls v1.8.3/go.mod h1:kncGGVhFaoGn5M3pFe3SXhZCzsbCJayNOH4UEqTKTko=
github.com/mholt/acmez/v3 v3.1.2 h1:auob8J/0FhmdClQicvJvuDavgd5ezwLBfKuYmynhYzc=
github.com/mholt/acmez/v3 v3.1.2/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
github.com/miekg/dns v1.1.67 h1:kg0EHj0G4bfT5/oOys6HhZw4vmMlnoZ+gDu8tJ/AlI0=
@@ -101,8 +101,8 @@ github.com/sagernet/sing-shadowsocks2 v0.2.1 h1:dWV9OXCeFPuYGHb6IRqlSptVnSzOelnq
github.com/sagernet/sing-shadowsocks2 v0.2.1/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 h1:tK+75l64tm9WvEFrYRE1t0YxoFdWQqw/h7Uhzj0vJ+w=
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11/go.mod h1:sWqKnGlMipCHaGsw1sTTlimyUpgzP4WP3pjhCsYt9oA=
-github.com/sagernet/sing-tun v0.7.2 h1:uJkAZM0KBqIYzrq077QGqdvj/+4i/pMOx6Pnx0jYqAs=
-github.com/sagernet/sing-tun v0.7.2/go.mod h1:pUEjh9YHQ2gJT6Lk0TYDklh3WJy7lz+848vleGM3JPM=
+github.com/sagernet/sing-tun v0.7.3 h1:MFnAir+l24ElEyxdfwtY8mqvUUL9nPnL9TDYLkOmVes=
+github.com/sagernet/sing-tun v0.7.3/go.mod h1:pUEjh9YHQ2gJT6Lk0TYDklh3WJy7lz+848vleGM3JPM=
github.com/sagernet/sing-vmess v0.2.7 h1:2ee+9kO0xW5P4mfe6TYVWf9VtY8k1JhNysBqsiYj0sk=
github.com/sagernet/sing-vmess v0.2.7/go.mod h1:5aYoOtYksAyS0NXDm0qKeTYW1yoE1bJVcv+XLcVoyJs=
github.com/sagernet/smux v1.5.34-mod.2 h1:gkmBjIjlJ2zQKpLigOkFur5kBKdV6bNRoFu2WkltRQ4=
From 963418f9e33f0264cd5958b76bab85e79c96cd49 Mon Sep 17 00:00:00 2001
From: armv9 <48624112+arm64v8a@users.noreply.github.com>
Date: Sun, 26 Oct 2025 13:28:30 +0900
Subject: [PATCH 08/18] Add permission for some CN ROMs
---
app/src/main/AndroidManifest.xml | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index f969dea48..def29c24c 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -22,7 +22,9 @@
+ tools:ignore="PackageVisibilityPolicy" />
+
+
From ec76238dd1f9522675d1e1be89df7e00648f8856 Mon Sep 17 00:00:00 2001
From: armv9 <48624112+arm64v8a@users.noreply.github.com>
Date: Sun, 26 Oct 2025 14:29:08 +0900
Subject: [PATCH 09/18] Add a hint for an empty app list
---
.../sagernet/ui/AppListActivity.kt | 32 ++++++++++++-------
.../sagernet/ui/AppManagerActivity.kt | 15 ++++++++-
app/src/main/res/layout/layout_app_list.xml | 4 +++
.../res/layout/layout_app_placeholder.xml | 25 +++++++++++++++
app/src/main/res/layout/layout_apps.xml | 4 +++
app/src/main/res/values-zh-rCN/strings.xml | 2 ++
app/src/main/res/values/strings.xml | 2 ++
7 files changed, 72 insertions(+), 12 deletions(-)
create mode 100644 app/src/main/res/layout/layout_app_placeholder.xml
diff --git a/app/src/main/java/io/nekohasekai/sagernet/ui/AppListActivity.kt b/app/src/main/java/io/nekohasekai/sagernet/ui/AppListActivity.kt
index 107b27e3d..86eb9a705 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/ui/AppListActivity.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/ui/AppListActivity.kt
@@ -2,7 +2,6 @@ package io.nekohasekai.sagernet.ui
import android.content.Intent
import android.content.pm.ApplicationInfo
-import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import android.os.Bundle
@@ -45,6 +44,11 @@ import kotlin.coroutines.coroutineContext
class AppListActivity : ThemedActivity() {
companion object {
private const val SWITCH = "switch"
+
+ private val cachedApps
+ get() = PackageCache.installedPackages.toMutableMap().apply {
+ remove(BuildConfig.APPLICATION_ID)
+ }
}
private class ProxiedApp(
@@ -96,7 +100,7 @@ class AppListActivity : ThemedActivity() {
var filteredApps = apps
suspend fun reload() {
- apps = getCachedApps().mapNotNull { (packageName, packageInfo) ->
+ apps = cachedApps.mapNotNull { (packageName, packageInfo) ->
coroutineContext[Job]!!.ensureActive()
packageInfo.applicationInfo?.let { ProxiedApp(packageManager, it, packageName) }
}.sortedWith(compareBy({ !isProxiedApp(it) }, { it.name.toString() }))
@@ -156,7 +160,7 @@ class AppListActivity : ThemedActivity() {
private fun initProxiedUids(str: String = DataStore.routePackages) {
proxiedUids.clear()
- val apps = getCachedApps()
+ val apps = cachedApps
for (line in str.lineSequence()) {
val app = (apps[line] ?: continue)
val uid = app.applicationInfo?.uid ?: continue
@@ -174,14 +178,12 @@ class AppListActivity : ThemedActivity() {
val adapter = binding.list.adapter as AppsAdapter
withContext(Dispatchers.IO) { adapter.reload() }
adapter.filter.filter(binding.search.text?.toString() ?: "")
- binding.list.crossFadeFrom(loading)
- }
- }
-
- fun getCachedApps(): MutableMap {
- val packages = PackageCache.installedPackages
- return packages.toMutableMap().apply {
- remove(BuildConfig.APPLICATION_ID)
+ if (apps.isEmpty()) {
+ binding.list.visibility = View.GONE
+ binding.appPlaceholder.root.crossFadeFrom(loading)
+ } else {
+ binding.list.crossFadeFrom(loading)
+ }
}
}
@@ -191,6 +193,14 @@ class AppListActivity : ThemedActivity() {
binding = LayoutAppListBinding.inflate(layoutInflater)
setContentView(binding.root)
+ binding.appPlaceholder.openSettings.setOnClickListener {
+ val intent =
+ Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
+ data = android.net.Uri.fromParts("package", packageName, null)
+ }
+ startActivity(intent)
+ }
+
setSupportActionBar(binding.toolbar)
supportActionBar?.apply {
setTitle(R.string.select_apps)
diff --git a/app/src/main/java/io/nekohasekai/sagernet/ui/AppManagerActivity.kt b/app/src/main/java/io/nekohasekai/sagernet/ui/AppManagerActivity.kt
index e212cbe9c..e5eb258ce 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/ui/AppManagerActivity.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/ui/AppManagerActivity.kt
@@ -184,7 +184,12 @@ class AppManagerActivity : ThemedActivity() {
val adapter = binding.list.adapter as AppsAdapter
withContext(Dispatchers.IO) { adapter.reload() }
adapter.filter.filter(binding.search.text?.toString() ?: "")
- binding.list.crossFadeFrom(loading)
+ if (apps.isEmpty()) {
+ binding.list.visibility = View.GONE
+ binding.appPlaceholder.root.crossFadeFrom(loading)
+ } else {
+ binding.list.crossFadeFrom(loading)
+ }
}
}
@@ -194,6 +199,14 @@ class AppManagerActivity : ThemedActivity() {
binding = LayoutAppsBinding.inflate(layoutInflater)
setContentView(binding.root)
+ binding.appPlaceholder.openSettings.setOnClickListener {
+ val intent =
+ Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
+ data = android.net.Uri.fromParts("package", packageName, null)
+ }
+ startActivity(intent)
+ }
+
setSupportActionBar(binding.toolbar)
supportActionBar?.apply {
setTitle(R.string.proxied_apps)
diff --git a/app/src/main/res/layout/layout_app_list.xml b/app/src/main/res/layout/layout_app_list.xml
index 45432fa45..4128a887f 100644
--- a/app/src/main/res/layout/layout_app_list.xml
+++ b/app/src/main/res/layout/layout_app_list.xml
@@ -116,4 +116,8 @@
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:listitem="@layout/layout_apps_item" />
+
+
diff --git a/app/src/main/res/layout/layout_app_placeholder.xml b/app/src/main/res/layout/layout_app_placeholder.xml
new file mode 100644
index 000000000..456ac8a2b
--- /dev/null
+++ b/app/src/main/res/layout/layout_app_placeholder.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/layout_apps.xml b/app/src/main/res/layout/layout_apps.xml
index 1dd32b265..e648248a2 100644
--- a/app/src/main/res/layout/layout_apps.xml
+++ b/app/src/main/res/layout/layout_apps.xml
@@ -153,4 +153,8 @@
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:listitem="@layout/layout_apps_item" />
+
+
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index cd92b7e10..29ee46388 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -495,4 +495,6 @@
恢复默认设置
恢复默认设置,但节点、分组等数据将保留。如需完全清除数据,请在系统设置中直接清除应用数据。
最小化
+ 无法读取已安装的应用。\n这通常是由于系统限制了应用的读取权限(例如某些中国厂商系统)。\n请在系统设置中授予权限。
+ 打开系统设置
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index a30c70328..cd2701bca 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -575,4 +575,6 @@
Restore default settings
Restore default settings, but data such as nodes and groups will be retained. To completely clear data, clear application data directly in the system settings.
Minimize
+ Unable to read installed apps.\nThis is usually because the system has restricted app read permissions.\nPlease grant permissions in the system settings.
+ Open System Settings
\ No newline at end of file
From 731064cf88d9bbf0fa89c0fbccd84df8f350806c Mon Sep 17 00:00:00 2001
From: armv9 <48624112+arm64v8a@users.noreply.github.com>
Date: Sun, 26 Oct 2025 14:55:29 +0900
Subject: [PATCH 10/18] Reload package cache when entering the app list
---
.../java/io/nekohasekai/sagernet/ui/AppListActivity.kt | 1 +
.../io/nekohasekai/sagernet/ui/AppManagerActivity.kt | 1 +
.../java/io/nekohasekai/sagernet/utils/PackageCache.kt | 9 ---------
3 files changed, 2 insertions(+), 9 deletions(-)
diff --git a/app/src/main/java/io/nekohasekai/sagernet/ui/AppListActivity.kt b/app/src/main/java/io/nekohasekai/sagernet/ui/AppListActivity.kt
index 86eb9a705..7f3892430 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/ui/AppListActivity.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/ui/AppListActivity.kt
@@ -100,6 +100,7 @@ class AppListActivity : ThemedActivity() {
var filteredApps = apps
suspend fun reload() {
+ PackageCache.reload()
apps = cachedApps.mapNotNull { (packageName, packageInfo) ->
coroutineContext[Job]!!.ensureActive()
packageInfo.applicationInfo?.let { ProxiedApp(packageManager, it, packageName) }
diff --git a/app/src/main/java/io/nekohasekai/sagernet/ui/AppManagerActivity.kt b/app/src/main/java/io/nekohasekai/sagernet/ui/AppManagerActivity.kt
index e5eb258ce..effbc5823 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/ui/AppManagerActivity.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/ui/AppManagerActivity.kt
@@ -106,6 +106,7 @@ class AppManagerActivity : ThemedActivity() {
var filteredApps = apps
suspend fun reload() {
+ PackageCache.reload()
apps = cachedApps.mapNotNull { (packageName, packageInfo) ->
coroutineContext[Job]!!.ensureActive()
packageInfo.applicationInfo?.let { ProxiedApp(packageManager, it, packageName) }
diff --git a/app/src/main/java/io/nekohasekai/sagernet/utils/PackageCache.kt b/app/src/main/java/io/nekohasekai/sagernet/utils/PackageCache.kt
index a2e09ef58..c5e9370c7 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/utils/PackageCache.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/utils/PackageCache.kt
@@ -67,15 +67,6 @@ object PackageCache {
operator fun get(uid: Int) = uidMap[uid]
operator fun get(packageName: String) = packageMap[packageName]
- suspend fun awaitLoad() {
- if (::packageMap.isInitialized) {
- return
- }
- loaded.withLock {
- // just await
- }
- }
-
fun awaitLoadSync() {
if (::packageMap.isInitialized) {
return
From 6c0b8dd450106895a6fc9dd817537373e689bad7 Mon Sep 17 00:00:00 2001
From: armv9 <48624112+arm64v8a@users.noreply.github.com>
Date: Sun, 26 Oct 2025 14:55:39 +0900
Subject: [PATCH 11/18] update preview version
---
nb4a.properties | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nb4a.properties b/nb4a.properties
index 0701e3f13..c032774fd 100644
--- a/nb4a.properties
+++ b/nb4a.properties
@@ -1,4 +1,4 @@
PACKAGE_NAME=moe.nb4a
VERSION_NAME=1.4.0
-PRE_VERSION_NAME=pre-1.4.1-20251021-1
+PRE_VERSION_NAME=pre-1.4.1-20251026-2
VERSION_CODE=44
From 2c2b6dc1a839d24b08f3d8f384e4a88e84f1bfd2 Mon Sep 17 00:00:00 2001
From: armv9 <48624112+arm64v8a@users.noreply.github.com>
Date: Thu, 30 Oct 2025 16:22:25 +0900
Subject: [PATCH 12/18] 1.4.1
---
buildScript/lib/core/get_source_env.sh | 2 +-
libcore/go.mod | 4 ++--
libcore/go.sum | 8 ++++----
nb4a.properties | 4 ++--
4 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/buildScript/lib/core/get_source_env.sh b/buildScript/lib/core/get_source_env.sh
index 5c7bc0be9..d3851c00c 100644
--- a/buildScript/lib/core/get_source_env.sh
+++ b/buildScript/lib/core/get_source_env.sh
@@ -1,2 +1,2 @@
-export COMMIT_SING_BOX="98c74d272a6b61abc120ff8163ab2ead0bb0ce96"
+export COMMIT_SING_BOX="9beb42f553ebdf575f497c01c33ffa7b6df17efb"
export COMMIT_LIBNEKO="1c47a3af71990a7b2192e03292b4d246c308ef0b"
diff --git a/libcore/go.mod b/libcore/go.mod
index daaf48eee..12b9d8298 100644
--- a/libcore/go.mod
+++ b/libcore/go.mod
@@ -8,8 +8,8 @@ require (
github.com/matsuridayo/libneko v1.0.0 // replaced
github.com/miekg/dns v1.1.67
github.com/oschwald/maxminddb-golang v1.13.1
- github.com/sagernet/quic-go v0.52.0-sing-box-mod.2
- github.com/sagernet/sing v0.7.12
+ github.com/sagernet/quic-go v0.52.0-sing-box-mod.3
+ github.com/sagernet/sing v0.7.13
github.com/sagernet/sing-box v1.0.0 // replaced
github.com/sagernet/sing-tun v0.7.3
github.com/ulikunitz/xz v0.5.15
diff --git a/libcore/go.sum b/libcore/go.sum
index b516d865b..55e9d7d7c 100644
--- a/libcore/go.sum
+++ b/libcore/go.sum
@@ -86,11 +86,11 @@ github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZN
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I=
github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8=
-github.com/sagernet/quic-go v0.52.0-sing-box-mod.2 h1:QTPr/ptUPsgregVfFXReBFrhv/8U83deZG8urQ7pWYI=
-github.com/sagernet/quic-go v0.52.0-sing-box-mod.2/go.mod h1:OV+V5kEBb8kJS7k29MzDu6oj9GyMc7HA07sE1tedxz4=
+github.com/sagernet/quic-go v0.52.0-sing-box-mod.3 h1:ySqffGm82rPqI1TUPqmtHIYd12pfEGScygnOxjTL56w=
+github.com/sagernet/quic-go v0.52.0-sing-box-mod.3/go.mod h1:OV+V5kEBb8kJS7k29MzDu6oj9GyMc7HA07sE1tedxz4=
github.com/sagernet/sing v0.6.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
-github.com/sagernet/sing v0.7.12 h1:MpMbO56crPRZTbltoj1wGk4Xj9+GiwH1wTO4s3fz1EA=
-github.com/sagernet/sing v0.7.12/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
+github.com/sagernet/sing v0.7.13 h1:XNYgd8e3cxMULs/LLJspdn/deHrnPWyrrglNHeCUAYM=
+github.com/sagernet/sing v0.7.13/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/sing-mux v0.3.3 h1:YFgt9plMWzH994BMZLmyKL37PdIVaIilwP0Jg+EcLfw=
github.com/sagernet/sing-mux v0.3.3/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA=
github.com/sagernet/sing-quic v0.5.2-0.20250909083218-00a55617c0fb h1:5Wx3XeTiKrrrcrAky7Hc1bO3CGxrvho2Vu5b/adlEIM=
diff --git a/nb4a.properties b/nb4a.properties
index c032774fd..aab000694 100644
--- a/nb4a.properties
+++ b/nb4a.properties
@@ -1,4 +1,4 @@
PACKAGE_NAME=moe.nb4a
-VERSION_NAME=1.4.0
+VERSION_NAME=1.4.1
PRE_VERSION_NAME=pre-1.4.1-20251026-2
-VERSION_CODE=44
+VERSION_CODE=45
From 894933b483353865fac5348dccd4e82071bd5a61 Mon Sep 17 00:00:00 2001
From: armv9 <48624112+arm64v8a@users.noreply.github.com>
Date: Mon, 2 Feb 2026 14:57:53 +0900
Subject: [PATCH 13/18] update core
---
buildScript/lib/core/get_source_env.sh | 2 +-
libcore/go.mod | 12 ++++-----
libcore/go.sum | 34 +++++++++-----------------
3 files changed, 19 insertions(+), 29 deletions(-)
diff --git a/buildScript/lib/core/get_source_env.sh b/buildScript/lib/core/get_source_env.sh
index d3851c00c..2a369106f 100644
--- a/buildScript/lib/core/get_source_env.sh
+++ b/buildScript/lib/core/get_source_env.sh
@@ -1,2 +1,2 @@
-export COMMIT_SING_BOX="9beb42f553ebdf575f497c01c33ffa7b6df17efb"
+export COMMIT_SING_BOX="aed32ee3066cdbc7d471e3e0415c5134088962df"
export COMMIT_LIBNEKO="1c47a3af71990a7b2192e03292b4d246c308ef0b"
diff --git a/libcore/go.mod b/libcore/go.mod
index 12b9d8298..ddf89a1f6 100644
--- a/libcore/go.mod
+++ b/libcore/go.mod
@@ -9,9 +9,9 @@ require (
github.com/miekg/dns v1.1.67
github.com/oschwald/maxminddb-golang v1.13.1
github.com/sagernet/quic-go v0.52.0-sing-box-mod.3
- github.com/sagernet/sing v0.7.13
+ github.com/sagernet/sing v0.7.18
github.com/sagernet/sing-box v1.0.0 // replaced
- github.com/sagernet/sing-tun v0.7.3
+ github.com/sagernet/sing-tun v0.7.10
github.com/ulikunitz/xz v0.5.15
golang.org/x/mobile v0.0.0-20231108233038-35478a0c49da
golang.org/x/sys v0.35.0
@@ -43,7 +43,7 @@ require (
github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42 // indirect
github.com/mdlayher/socket v0.5.1 // indirect
github.com/metacubex/tfo-go v0.0.0-20250921095601-b102db4216c0 // indirect
- github.com/metacubex/utls v1.8.3 // indirect
+ github.com/metacubex/utls v1.8.4 // indirect
github.com/mholt/acmez/v3 v3.1.2 // indirect
github.com/quic-go/qpack v0.5.1 // indirect
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a // indirect
@@ -52,13 +52,13 @@ require (
github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb // indirect
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect
github.com/sagernet/nftables v0.3.0-beta.4 // indirect
- github.com/sagernet/sing-mux v0.3.3 // indirect
- github.com/sagernet/sing-quic v0.5.2-0.20250909083218-00a55617c0fb // indirect
+ github.com/sagernet/sing-mux v0.3.4 // indirect
+ github.com/sagernet/sing-quic v0.5.2 // indirect
github.com/sagernet/sing-shadowsocks v0.2.8 // indirect
github.com/sagernet/sing-shadowsocks2 v0.2.1 // indirect
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 // indirect
github.com/sagernet/sing-vmess v0.2.7 // indirect
- github.com/sagernet/smux v1.5.34-mod.2 // indirect
+ github.com/sagernet/smux v1.5.50-sing-box-mod.1 // indirect
github.com/sagernet/wireguard-go v0.0.1-beta.7 // indirect
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 // indirect
github.com/vishvananda/netns v0.0.5 // indirect
diff --git a/libcore/go.sum b/libcore/go.sum
index 55e9d7d7c..dcef8d1db 100644
--- a/libcore/go.sum
+++ b/libcore/go.sum
@@ -11,7 +11,6 @@ github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/X
github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo=
github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
@@ -61,8 +60,8 @@ github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
github.com/metacubex/tfo-go v0.0.0-20250921095601-b102db4216c0 h1:Ui+/2s5Qz0lSnDUBmEL12M5Oi/PzvFxGTNohm8ZcsmE=
github.com/metacubex/tfo-go v0.0.0-20250921095601-b102db4216c0/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw=
-github.com/metacubex/utls v1.8.3 h1:0m/yCxm3SK6kWve2lKiFb1pue1wHitJ8sQQD4Ikqde4=
-github.com/metacubex/utls v1.8.3/go.mod h1:kncGGVhFaoGn5M3pFe3SXhZCzsbCJayNOH4UEqTKTko=
+github.com/metacubex/utls v1.8.4 h1:HmL9nUApDdWSkgUyodfwF6hSjtiwCGGdyhaSpEejKpg=
+github.com/metacubex/utls v1.8.4/go.mod h1:kncGGVhFaoGn5M3pFe3SXhZCzsbCJayNOH4UEqTKTko=
github.com/mholt/acmez/v3 v3.1.2 h1:auob8J/0FhmdClQicvJvuDavgd5ezwLBfKuYmynhYzc=
github.com/mholt/acmez/v3 v3.1.2/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
github.com/miekg/dns v1.1.67 h1:kg0EHj0G4bfT5/oOys6HhZw4vmMlnoZ+gDu8tJ/AlI0=
@@ -88,38 +87,30 @@ github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNen
github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8=
github.com/sagernet/quic-go v0.52.0-sing-box-mod.3 h1:ySqffGm82rPqI1TUPqmtHIYd12pfEGScygnOxjTL56w=
github.com/sagernet/quic-go v0.52.0-sing-box-mod.3/go.mod h1:OV+V5kEBb8kJS7k29MzDu6oj9GyMc7HA07sE1tedxz4=
-github.com/sagernet/sing v0.6.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
-github.com/sagernet/sing v0.7.13 h1:XNYgd8e3cxMULs/LLJspdn/deHrnPWyrrglNHeCUAYM=
-github.com/sagernet/sing v0.7.13/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
-github.com/sagernet/sing-mux v0.3.3 h1:YFgt9plMWzH994BMZLmyKL37PdIVaIilwP0Jg+EcLfw=
-github.com/sagernet/sing-mux v0.3.3/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA=
-github.com/sagernet/sing-quic v0.5.2-0.20250909083218-00a55617c0fb h1:5Wx3XeTiKrrrcrAky7Hc1bO3CGxrvho2Vu5b/adlEIM=
-github.com/sagernet/sing-quic v0.5.2-0.20250909083218-00a55617c0fb/go.mod h1:evP1e++ZG8TJHVV5HudXV4vWeYzGfCdF4HwSJZcdqkI=
+github.com/sagernet/sing v0.7.18 h1:iZHkaru1/MoHugx3G+9S3WG4owMewKO/KvieE2Pzk4E=
+github.com/sagernet/sing v0.7.18/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
+github.com/sagernet/sing-mux v0.3.4 h1:ZQplKl8MNXutjzbMVtWvWG31fohhgOfCuUZR4dVQ8+s=
+github.com/sagernet/sing-mux v0.3.4/go.mod h1:QvlKMyNBNrQoyX4x+gq028uPbLM2XeRpWtDsWBJbFSk=
+github.com/sagernet/sing-quic v0.5.2 h1:I3vlfRImhr0uLwRS3b3ib70RMG9FcXtOKKUDz3eKRWc=
+github.com/sagernet/sing-quic v0.5.2/go.mod h1:evP1e++ZG8TJHVV5HudXV4vWeYzGfCdF4HwSJZcdqkI=
github.com/sagernet/sing-shadowsocks v0.2.8 h1:PURj5PRoAkqeHh2ZW205RWzN9E9RtKCVCzByXruQWfE=
github.com/sagernet/sing-shadowsocks v0.2.8/go.mod h1:lo7TWEMDcN5/h5B8S0ew+r78ZODn6SwVaFhvB6H+PTI=
github.com/sagernet/sing-shadowsocks2 v0.2.1 h1:dWV9OXCeFPuYGHb6IRqlSptVnSzOelnqqs2gQ2/Qioo=
github.com/sagernet/sing-shadowsocks2 v0.2.1/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 h1:tK+75l64tm9WvEFrYRE1t0YxoFdWQqw/h7Uhzj0vJ+w=
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11/go.mod h1:sWqKnGlMipCHaGsw1sTTlimyUpgzP4WP3pjhCsYt9oA=
-github.com/sagernet/sing-tun v0.7.3 h1:MFnAir+l24ElEyxdfwtY8mqvUUL9nPnL9TDYLkOmVes=
-github.com/sagernet/sing-tun v0.7.3/go.mod h1:pUEjh9YHQ2gJT6Lk0TYDklh3WJy7lz+848vleGM3JPM=
+github.com/sagernet/sing-tun v0.7.10 h1:lLBaS9uL0mK/FCGDe3N4oKQxjMGfmv3u2/6jKtmq4Pw=
+github.com/sagernet/sing-tun v0.7.10/go.mod h1:pUEjh9YHQ2gJT6Lk0TYDklh3WJy7lz+848vleGM3JPM=
github.com/sagernet/sing-vmess v0.2.7 h1:2ee+9kO0xW5P4mfe6TYVWf9VtY8k1JhNysBqsiYj0sk=
github.com/sagernet/sing-vmess v0.2.7/go.mod h1:5aYoOtYksAyS0NXDm0qKeTYW1yoE1bJVcv+XLcVoyJs=
-github.com/sagernet/smux v1.5.34-mod.2 h1:gkmBjIjlJ2zQKpLigOkFur5kBKdV6bNRoFu2WkltRQ4=
-github.com/sagernet/smux v1.5.34-mod.2/go.mod h1:0KW0+R+ycvA2INW4gbsd7BNyg+HEfLIAxa5N02/28Zc=
+github.com/sagernet/smux v1.5.50-sing-box-mod.1 h1:XkJcivBC9V4wBjiGXIXZ229aZCU1hzcbp6kSkkyQ478=
+github.com/sagernet/smux v1.5.50-sing-box-mod.1/go.mod h1:NjhsCEWedJm7eFLyhuBgIEzwfhRmytrUoiLluxs5Sk8=
github.com/sagernet/wireguard-go v0.0.1-beta.7 h1:ltgBwYHfr+9Wz1eG59NiWnHrYEkDKHG7otNZvu85DXI=
github.com/sagernet/wireguard-go v0.0.1-beta.7/go.mod h1:jGXij2Gn2wbrWuYNUmmNhf1dwcZtvyAvQoe8Xd8MbUo=
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 h1:6uUiZcDRnZSAegryaUGwPC/Fj13JSHwiTftrXhMmYOc=
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854/go.mod h1:LtfoSK3+NG57tvnVEHgcuBW9ujgE8enPSgzgwStwCAA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
-github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
-github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY=
@@ -173,7 +164,6 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
From 01bf49f76569ebf705d89f1d9d78d1fcf5c8773e Mon Sep 17 00:00:00 2001
From: armv9 <48624112+arm64v8a@users.noreply.github.com>
Date: Mon, 2 Feb 2026 15:16:18 +0900
Subject: [PATCH 14/18] refactor configuration overriding during subscription
updates
---
.../io/nekohasekai/sagernet/fmt/AbstractBean.java | 4 ----
.../sagernet/fmt/hysteria/HysteriaBean.java | 11 -----------
.../sagernet/fmt/shadowsocks/ShadowsocksBean.java | 7 -------
.../sagernet/fmt/trojan_go/TrojanGoBean.java | 9 ---------
.../io/nekohasekai/sagernet/fmt/tuic/TuicFmt.kt | 14 ++++++++++++--
.../sagernet/fmt/v2ray/StandardV2RayBean.java | 15 ---------------
.../io/nekohasekai/sagernet/group/RawUpdater.kt | 4 +++-
7 files changed, 15 insertions(+), 49 deletions(-)
diff --git a/app/src/main/java/io/nekohasekai/sagernet/fmt/AbstractBean.java b/app/src/main/java/io/nekohasekai/sagernet/fmt/AbstractBean.java
index 53dda06c2..86e6fca9e 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/fmt/AbstractBean.java
+++ b/app/src/main/java/io/nekohasekai/sagernet/fmt/AbstractBean.java
@@ -144,8 +144,4 @@ public int hashCode() {
public String toString() {
return getClass().getSimpleName() + " " + JavaUtil.gson.toJson(this);
}
-
- public void applyFeatureSettings(AbstractBean other) {
- }
-
}
diff --git a/app/src/main/java/io/nekohasekai/sagernet/fmt/hysteria/HysteriaBean.java b/app/src/main/java/io/nekohasekai/sagernet/fmt/hysteria/HysteriaBean.java
index 6a5f83c03..2d2e19e1d 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/fmt/hysteria/HysteriaBean.java
+++ b/app/src/main/java/io/nekohasekai/sagernet/fmt/hysteria/HysteriaBean.java
@@ -150,17 +150,6 @@ public void deserialize(ByteBufferInput input) {
}
}
- @Override
- public void applyFeatureSettings(AbstractBean other) {
- if (!(other instanceof HysteriaBean)) return;
- HysteriaBean bean = ((HysteriaBean) other);
- bean.uploadMbps = uploadMbps;
- bean.downloadMbps = downloadMbps;
- bean.allowInsecure = allowInsecure;
- bean.disableMtuDiscovery = disableMtuDiscovery;
- bean.hopInterval = hopInterval;
- }
-
@Override
public String displayAddress() {
return NetsKt.wrapIPV6Host(serverAddress) + ":" + serverPorts;
diff --git a/app/src/main/java/io/nekohasekai/sagernet/fmt/shadowsocks/ShadowsocksBean.java b/app/src/main/java/io/nekohasekai/sagernet/fmt/shadowsocks/ShadowsocksBean.java
index 326662ffb..10c394f59 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/fmt/shadowsocks/ShadowsocksBean.java
+++ b/app/src/main/java/io/nekohasekai/sagernet/fmt/shadowsocks/ShadowsocksBean.java
@@ -50,13 +50,6 @@ public void deserialize(ByteBufferInput input) {
sUoT = input.readBoolean();
}
- @Override
- public void applyFeatureSettings(AbstractBean other) {
- if (!(other instanceof ShadowsocksBean)) return;
- ShadowsocksBean bean = ((ShadowsocksBean) other);
- bean.sUoT = sUoT;
- }
-
@NotNull
@Override
public ShadowsocksBean clone() {
diff --git a/app/src/main/java/io/nekohasekai/sagernet/fmt/trojan_go/TrojanGoBean.java b/app/src/main/java/io/nekohasekai/sagernet/fmt/trojan_go/TrojanGoBean.java
index f02f36388..fa4aaf4e5 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/fmt/trojan_go/TrojanGoBean.java
+++ b/app/src/main/java/io/nekohasekai/sagernet/fmt/trojan_go/TrojanGoBean.java
@@ -146,15 +146,6 @@ public void deserialize(ByteBufferInput input) {
}
}
- @Override
- public void applyFeatureSettings(AbstractBean other) {
- if (!(other instanceof TrojanGoBean)) return;
- TrojanGoBean bean = ((TrojanGoBean) other);
- if (allowInsecure) {
- bean.allowInsecure = true;
- }
- }
-
@NotNull
@Override
public TrojanGoBean clone() {
diff --git a/app/src/main/java/io/nekohasekai/sagernet/fmt/tuic/TuicFmt.kt b/app/src/main/java/io/nekohasekai/sagernet/fmt/tuic/TuicFmt.kt
index cad2147e6..b418a1b63 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/fmt/tuic/TuicFmt.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/fmt/tuic/TuicFmt.kt
@@ -17,11 +17,21 @@ fun parseTuic(url: String): TuicBean {
protocolVersion = 5
name = link.fragment
- uuid = link.username
- token = link.password
serverAddress = link.host
serverPort = link.port
+ val rawUser = link.username
+ val rawPass = link.password
+
+ if (rawUser.contains(":")) {
+ val parts = rawUser.split(":", limit = 2)
+ uuid = parts[0]
+ token = parts.getOrElse(1) { "" }
+ } else {
+ uuid = rawUser
+ token = rawPass
+ }
+
link.queryParameter("sni")?.let {
sni = it
}
diff --git a/app/src/main/java/io/nekohasekai/sagernet/fmt/v2ray/StandardV2RayBean.java b/app/src/main/java/io/nekohasekai/sagernet/fmt/v2ray/StandardV2RayBean.java
index 2f32c50f4..22adb2539 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/fmt/v2ray/StandardV2RayBean.java
+++ b/app/src/main/java/io/nekohasekai/sagernet/fmt/v2ray/StandardV2RayBean.java
@@ -258,21 +258,6 @@ public void deserialize(ByteBufferInput input) {
}
}
- @Override
- public void applyFeatureSettings(AbstractBean other) {
- if (!(other instanceof StandardV2RayBean)) return;
- StandardV2RayBean bean = ((StandardV2RayBean) other);
- bean.allowInsecure = allowInsecure;
- bean.utlsFingerprint = utlsFingerprint;
- bean.packetEncoding = packetEncoding;
- bean.enableECH = enableECH;
- bean.echConfig = echConfig;
- bean.enableMux = enableMux;
- bean.muxPadding = muxPadding;
- bean.muxType = muxType;
- bean.muxConcurrency = muxConcurrency;
- }
-
public boolean isVLESS() {
if (this instanceof VMessBean) {
Integer aid = ((VMessBean) this).alterId;
diff --git a/app/src/main/java/io/nekohasekai/sagernet/group/RawUpdater.kt b/app/src/main/java/io/nekohasekai/sagernet/group/RawUpdater.kt
index 28213489a..7ea0021e8 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/group/RawUpdater.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/group/RawUpdater.kt
@@ -161,7 +161,9 @@ object RawUpdater : GroupUpdater() {
if (toReplace.contains(name)) {
val entity = toReplace[name]!!
val existsBean = entity.requireBean()
- existsBean.applyFeatureSettings(bean)
+ // 更新订阅,保留自定义覆写设置
+ bean.customOutboundJson = existsBean.customOutboundJson
+ bean.customConfigJson = existsBean.customConfigJson
when {
existsBean != bean -> {
changed++
From 3211dc167c37a170aba001def3a3f43b55550a1e Mon Sep 17 00:00:00 2001
From: Svyatoslav Shchipunov
Date: Thu, 15 Jan 2026 01:59:07 +0700
Subject: [PATCH 15/18] feat: add on and off shortcuts for automatizations
---
app/src/main/AndroidManifest.xml | 18 ++++++
.../sagernet/ui/QuickDisableShortcut.kt | 58 +++++++++++++++++++
.../sagernet/ui/QuickEnableShortcut.kt | 58 +++++++++++++++++++
app/src/main/res/values-ar/strings.xml | 2 +
app/src/main/res/values-be/strings.xml | 2 +
app/src/main/res/values-de/strings.xml | 2 +
app/src/main/res/values-es/strings.xml | 4 +-
app/src/main/res/values-fa/strings.xml | 2 +
app/src/main/res/values-fr/strings.xml | 2 +
app/src/main/res/values-in/strings.xml | 2 +
app/src/main/res/values-ja/strings.xml | 2 +
app/src/main/res/values-ko/strings.xml | 2 +
app/src/main/res/values-nb-rNO/strings.xml | 2 +
app/src/main/res/values-ru/strings.xml | 2 +
app/src/main/res/values-tr/strings.xml | 2 +
app/src/main/res/values-uk/strings.xml | 2 +
app/src/main/res/values-zh-rCN/strings.xml | 2 +
app/src/main/res/values-zh-rHK/strings.xml | 2 +
app/src/main/res/values-zh-rTW/strings.xml | 2 +
app/src/main/res/values/strings.xml | 2 +
app/src/main/res/xml/shortcuts.xml | 20 +++++++
21 files changed, 189 insertions(+), 1 deletion(-)
create mode 100644 app/src/main/java/io/nekohasekai/sagernet/ui/QuickDisableShortcut.kt
create mode 100644 app/src/main/java/io/nekohasekai/sagernet/ui/QuickEnableShortcut.kt
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index def29c24c..69818c852 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -221,6 +221,24 @@
+
+
*
+ * Copyright (C) 2017 by Mygod Studio <[Email1]> *
+ * *
+ * This program is free software: you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation, either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see . *
+ * *
+ *******************************************************************************/
+
+package io.nekohasekai.sagernet.ui
+
+import android.app.Activity
+import android.content.pm.ShortcutManager
+import android.os.Build
+import android.os.Bundle
+import androidx.core.content.getSystemService
+import io.nekohasekai.sagernet.SagerNet
+import io.nekohasekai.sagernet.aidl.ISagerNetService
+import io.nekohasekai.sagernet.bg.BaseService
+import io.nekohasekai.sagernet.bg.SagerConnection
+
+class QuickDisableShortcut : Activity(), SagerConnection.Callback {
+ private val connection = SagerConnection(SagerConnection.CONNECTION_ID_SHORTCUT)
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ connection.connect(this, this)
+ if (Build.VERSION.SDK_INT >= 25) {
+ getSystemService()!!.reportShortcutUsed("disable")
+ }
+ }
+
+ override fun onServiceConnected(service: ISagerNetService) {
+ val state = BaseService.State.values()[service.state]
+ if (state.canStop) {
+ SagerNet.stopService()
+ }
+ finish()
+ }
+
+ override fun stateChanged(state: BaseService.State, profileName: String?, msg: String?) {}
+
+ override fun onDestroy() {
+ connection.disconnect(this)
+ super.onDestroy()
+ }
+}
diff --git a/app/src/main/java/io/nekohasekai/sagernet/ui/QuickEnableShortcut.kt b/app/src/main/java/io/nekohasekai/sagernet/ui/QuickEnableShortcut.kt
new file mode 100644
index 000000000..590868d18
--- /dev/null
+++ b/app/src/main/java/io/nekohasekai/sagernet/ui/QuickEnableShortcut.kt
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * *
+ * Copyright (C) 2017 by Max Lv <[Email0]> *
+ * Copyright (C) 2017 by Mygod Studio <[Email1]> *
+ * *
+ * This program is free software: you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation, either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see . *
+ * *
+ *******************************************************************************/
+
+package io.nekohasekai.sagernet.ui
+
+import android.app.Activity
+import android.content.pm.ShortcutManager
+import android.os.Build
+import android.os.Bundle
+import androidx.core.content.getSystemService
+import io.nekohasekai.sagernet.SagerNet
+import io.nekohasekai.sagernet.aidl.ISagerNetService
+import io.nekohasekai.sagernet.bg.BaseService
+import io.nekohasekai.sagernet.bg.SagerConnection
+
+class QuickEnableShortcut : Activity(), SagerConnection.Callback {
+ private val connection = SagerConnection(SagerConnection.CONNECTION_ID_SHORTCUT)
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ connection.connect(this, this)
+ if (Build.VERSION.SDK_INT >= 25) {
+ getSystemService()!!.reportShortcutUsed("enable")
+ }
+ }
+
+ override fun onServiceConnected(service: ISagerNetService) {
+ val state = BaseService.State.values()[service.state]
+ if (state == BaseService.State.Stopped) {
+ SagerNet.startService()
+ }
+ finish()
+ }
+
+ override fun stateChanged(state: BaseService.State, profileName: String?, msg: String?) {}
+
+ override fun onDestroy() {
+ connection.disconnect(this)
+ super.onDestroy()
+ }
+}
diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml
index 0d7e634ea..b756ddc32 100644
--- a/app/src/main/res/values-ar/strings.xml
+++ b/app/src/main/res/values-ar/strings.xml
@@ -23,6 +23,8 @@
أسم المجموعة
الجلاد
تبديل
+ تمكين
+ تعطيل
غير مجمعة
وثيقة
موضوع
diff --git a/app/src/main/res/values-be/strings.xml b/app/src/main/res/values-be/strings.xml
index 56567ae01..a140392cc 100644
--- a/app/src/main/res/values-be/strings.xml
+++ b/app/src/main/res/values-be/strings.xml
@@ -100,6 +100,8 @@
Трафік
Перамыкач
Пераключыць
+ Уключыць
+ Выключыць
Разгрупаваны
Дакумент
Тэма
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index baa382951..4bd07aeb5 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -5,6 +5,8 @@
Gruppen name
Umschalter
Umschalten
+ Aktivieren
+ Deaktivieren
Ungruppiert
Dokument
Style
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index e0301bcf7..fda3913b3 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -14,7 +14,9 @@
Tema
Documento
Desagrupado
- Alternar
+ Alternar
+ Habilitar
+ Deshabilitar
Conmutador
Tráfico
diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml
index de972072d..5e61ffe05 100644
--- a/app/src/main/res/values-fa/strings.xml
+++ b/app/src/main/res/values-fa/strings.xml
@@ -17,6 +17,8 @@
مستندات
گروه پیشفرض
تغییر وضعیت
+ فعالسازی
+ غیرفعالسازی
سوییچر
ترافیک
sing-box تنظیمات
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 86f4728be..8a69b076d 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -57,6 +57,8 @@
Mettre à jour
Nom du groupe
Basculer
+ Activer
+ Désactiver
Dégroupé
Document
Thème
diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml
index b52bb0c11..d09ed3ca7 100644
--- a/app/src/main/res/values-in/strings.xml
+++ b/app/src/main/res/values-in/strings.xml
@@ -83,6 +83,8 @@
Nama grup
Pengalih
Beralih
+ Aktifkan
+ Nonaktifkan
Tidak bergrup
Dokumen
Tema
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index bcd7649b9..10f7b36c1 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -35,6 +35,8 @@
グループ名
スイッチャー
トグル
+ 有効化
+ 無効化
グループ化されていない
ドキュメント(英語)
テーマ
diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml
index 742d825ff..edc95c6d6 100644
--- a/app/src/main/res/values-ko/strings.xml
+++ b/app/src/main/res/values-ko/strings.xml
@@ -9,6 +9,8 @@
프로젝트
코틀린에 기록 된 안드로이드에 대한 보편적 인 프록시 도구 체인.
비녀장
+ 활성화
+ 비활성화
그룹 해제
문서
테마
diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml
index f9472e690..8b07363b1 100644
--- a/app/src/main/res/values-nb-rNO/strings.xml
+++ b/app/src/main/res/values-nb-rNO/strings.xml
@@ -319,6 +319,8 @@
Oppdater
Bytter
Veksle
+ Aktiver
+ Deaktiver
Konfigurasjon
Den universelle proxy -verktøykjeden for Android, skrevet i Kotlin.
Raffinering
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index c45db7492..bc8c4b2ae 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -342,6 +342,8 @@
Настройки сервера
Прокси-цепочка
Переключить
+Включить
+Выключить
Не удалось запустить службу VPN. Возможно, вам потребуется перезагрузить устройство.
Отключить WakeLock
Удаленный DNS
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index 2afcf4b51..891a0a128 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -71,6 +71,8 @@
Değiştirici
Grup adı
Aç/Kapat
+ Etkinleştir
+ Devre Dışı Bırak
Gruplandırılmamış
Döküman
Tema
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 023942460..400d69374 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -17,6 +17,8 @@
Документація
Без групи
Перемикач
+ Увімкнути
+ Вимкнути
Перемикач
Трафік
Панель керування sing-box
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index 29ee46388..a9f0c1cba 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -12,6 +12,8 @@
关于
未分组
切换
+ 启用
+ 禁用
分组名
更新
diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml
index 840ee0f1c..bcf2c79dc 100644
--- a/app/src/main/res/values-zh-rHK/strings.xml
+++ b/app/src/main/res/values-zh-rHK/strings.xml
@@ -75,6 +75,8 @@
密碼(可選)
開始
切換
+ 啟用
+ 停用
編輯
WebSocket 設定
顯示直連速度
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index 695e6cf58..de521bcbd 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -11,6 +11,8 @@
群組
關於
切換
+ 啟用
+ 停用
群組名稱
更新
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index cd2701bca..1995ff051 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -17,6 +17,8 @@
Document
Ungrouped
Toggle
+ Enable
+ Disable
Switcher
Traffic
sing-box Dashboard
diff --git a/app/src/main/res/xml/shortcuts.xml b/app/src/main/res/xml/shortcuts.xml
index c4a2badf9..b6b9b2b9b 100644
--- a/app/src/main/res/xml/shortcuts.xml
+++ b/app/src/main/res/xml/shortcuts.xml
@@ -10,6 +10,26 @@
android:targetClass="io.nekohasekai.sagernet.QuickToggleShortcut"
android:targetPackage="moe.nb4a" />
+
+
+
+
+
+
Date: Mon, 2 Feb 2026 16:59:06 +0900
Subject: [PATCH 16/18] many optimize
---
.../nekohasekai/sagernet/group/RawUpdater.kt | 9 +++++++
.../nekohasekai/sagernet/ui/AssetsActivity.kt | 7 ++++-
.../nekohasekai/sagernet/ui/GroupFragment.kt | 15 +++++++----
.../matsuri/nb4a/proxy/config/ConfigBean.java | 12 ++++++++-
libcore/ech/ech.go | 26 ++++++++-----------
libcore/http.go | 18 +++----------
6 files changed, 50 insertions(+), 37 deletions(-)
diff --git a/app/src/main/java/io/nekohasekai/sagernet/group/RawUpdater.kt b/app/src/main/java/io/nekohasekai/sagernet/group/RawUpdater.kt
index 7ea0021e8..a7597999e 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/group/RawUpdater.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/group/RawUpdater.kt
@@ -477,6 +477,15 @@ object RawUpdater : GroupUpdater() {
}
}
}
+
+ "ech-opts" -> (opt.value as? Map)?.also {
+ for (echOpt in it) {
+ when (echOpt.key) {
+ "enable" -> bean.enableECH =
+ echOpt.value.toString() == "true"
+ }
+ }
+ }
}
}
proxies.add(bean)
diff --git a/app/src/main/java/io/nekohasekai/sagernet/ui/AssetsActivity.kt b/app/src/main/java/io/nekohasekai/sagernet/ui/AssetsActivity.kt
index d63848289..25f2b6644 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/ui/AssetsActivity.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/ui/AssetsActivity.kt
@@ -211,7 +211,12 @@ class AssetsActivity : ThemedActivity() {
val localVersion = if (file.isFile) {
if (versionFile.isFile) {
- versionFile.readText().trim()
+ try {
+ versionFile.readText().trim()
+ } catch (e: Throwable) {
+ snackbar(e.readableMessage)
+ ""
+ }
} else {
"Unknown-" + DateFormat.getDateFormat(app).format(Date(file.lastModified()))
}
diff --git a/app/src/main/java/io/nekohasekai/sagernet/ui/GroupFragment.kt b/app/src/main/java/io/nekohasekai/sagernet/ui/GroupFragment.kt
index ffc1f55ba..840234b54 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/ui/GroupFragment.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/ui/GroupFragment.kt
@@ -476,12 +476,17 @@ class GroupFragment : ToolbarFragment(R.layout.layout_group),
used += toLong()
}
val total = get("total=([0-9]+)")?.toLong() ?: 0
+ val remain = total - used
if (used > 0 || total > 0) {
- text += getString(
- R.string.subscription_traffic,
- used.toBytesString(),
- (total - used).toBytesString()
- )
+ text += if (remain > 0) {
+ getString(
+ R.string.subscription_traffic,
+ used.toBytesString(),
+ remain.toBytesString()
+ )
+ } else {
+ getString(R.string.subscription_used, used.toBytesString())
+ }
}
get("expire=([0-9]+)")?.apply {
text += "\n"
diff --git a/app/src/main/java/moe/matsuri/nb4a/proxy/config/ConfigBean.java b/app/src/main/java/moe/matsuri/nb4a/proxy/config/ConfigBean.java
index b3f18a0f2..df436de76 100644
--- a/app/src/main/java/moe/matsuri/nb4a/proxy/config/ConfigBean.java
+++ b/app/src/main/java/moe/matsuri/nb4a/proxy/config/ConfigBean.java
@@ -4,6 +4,7 @@
import com.esotericsoftware.kryo.io.ByteBufferInput;
import com.esotericsoftware.kryo.io.ByteBufferOutput;
+import com.google.gson.JsonObject;
import org.jetbrains.annotations.NotNull;
@@ -49,7 +50,16 @@ public String displayName() {
}
public String displayType() {
- return type == 0 ? "sing-box config" : "sing-box outbound";
+ if (type != null && type == 1 && JavaUtil.isNotBlank(config)) {
+ try {
+ JsonObject json = JavaUtil.gson.fromJson(config, JsonObject.class);
+ if (json != null && json.has("type")) {
+ return json.get("type").getAsString() + " (sing-box)";
+ }
+ } catch (Exception ignored) {
+ }
+ }
+ return type != null && type == 0 ? "sing-box config" : "sing-box outbound";
}
@NotNull
diff --git a/libcore/ech/ech.go b/libcore/ech/ech.go
index 1dbf50adb..0094208de 100644
--- a/libcore/ech/ech.go
+++ b/libcore/ech/ech.go
@@ -4,6 +4,7 @@ import (
"context"
"crypto/tls"
"encoding/base64"
+ "log"
"net"
"os"
@@ -29,21 +30,16 @@ func NewECHClientConfig(domain string, tlsConfig *tls.Config, localDnsTransport
}
}
-// ClientHandshake 封装 TLS 握手
-func (s *ECHClientConfig) ClientHandshake(ctx context.Context, conn net.Conn) (*tls.Conn, error) {
- tlsConn, err := s.fetchAndHandshake(ctx, conn)
+func (s *ECHClientConfig) Client(ctx context.Context, conn net.Conn) (*tls.Conn, error) {
+ err := s.fetchEchKeys(ctx, conn)
if err != nil {
- return nil, err
+ // allow empty ech keys
+ log.Println("fetchEchKeys:", err)
}
- err = tlsConn.HandshakeContext(ctx)
- if err != nil {
- return nil, err
- }
- return tlsConn, nil
+ return tls.Client(conn, s.Config), nil
}
-// fetchAndHandshake 查询 ECHConfigList 并完成 TLS 连接
-func (s *ECHClientConfig) fetchAndHandshake(ctx context.Context, conn net.Conn) (*tls.Conn, error) {
+func (s *ECHClientConfig) fetchEchKeys(ctx context.Context, conn net.Conn) error {
message := &mDNS.Msg{
MsgHdr: mDNS.MsgHdr{
RecursionDesired: true,
@@ -57,14 +53,14 @@ func (s *ECHClientConfig) fetchAndHandshake(ctx context.Context, conn net.Conn)
},
}
if s.localDnsTransport == nil {
- return nil, os.ErrInvalid
+ return os.ErrInvalid
}
response, err := s.localDnsTransport.Exchange(ctx, message)
if err != nil {
- return nil, exceptions.Cause(err, "fetch ECH config list")
+ return exceptions.Cause(err, "fetch ECH config list")
}
if response.Rcode != mDNS.RcodeSuccess {
- return nil, exceptions.Cause(dns.RcodeError(response.Rcode), "fetch ECH config list")
+ return exceptions.Cause(dns.RcodeError(response.Rcode), "fetch ECH config list")
}
for _, rr := range response.Answer {
switch resource := rr.(type) {
@@ -79,5 +75,5 @@ func (s *ECHClientConfig) fetchAndHandshake(ctx context.Context, conn net.Conn)
}
}
}
- return tls.Client(conn, s.Config), nil
+ return nil
}
diff --git a/libcore/http.go b/libcore/http.go
index ce6fba6e9..c4a8db500 100644
--- a/libcore/http.go
+++ b/libcore/http.go
@@ -240,17 +240,7 @@ func (r *httpRequest) doH3Direct() (HTTPResponse, error) {
var mu sync.Mutex
funcs := []requestFunc{
- // 普通,不再重试 socks5
- func() (response *http.Response, err error) {
- request := r.request.Clone(context.Background())
- h1h2Client := &http.Client{
- Transport: &http.Transport{
- DisableKeepAlives: true,
- },
- }
- return h1h2Client.Do(request)
- },
- // ECH HTTPS
+ // Http(s) With Ech
func() (response *http.Response, err error) {
request := r.request.Clone(context.Background())
echClient := &http.Client{
@@ -266,7 +256,7 @@ func (r *httpRequest) doH3Direct() (HTTPResponse, error) {
domain = host
}
echTls := ech.NewECHClientConfig(domain, &r.tls, gLocalDNSTransport)
- return echTls.ClientHandshake(ctx, c)
+ return echTls.Client(ctx, c)
},
DisableKeepAlives: true,
},
@@ -307,10 +297,8 @@ func (r *httpRequest) doH3Direct() (HTTPResponse, error) {
var t string
switch i {
case 0:
- t = "h1h2"
+ t = "http(s)"
case 1:
- t = "ech"
- case 2:
t = "h3"
}
From a75155c0fb8b5719ab2034efb787a910a574f8bf Mon Sep 17 00:00:00 2001
From: armv9 <48624112+arm64v8a@users.noreply.github.com>
Date: Mon, 2 Feb 2026 17:07:38 +0900
Subject: [PATCH 17/18] update preview version
---
nb4a.properties | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nb4a.properties b/nb4a.properties
index aab000694..fb38d97eb 100644
--- a/nb4a.properties
+++ b/nb4a.properties
@@ -1,4 +1,4 @@
PACKAGE_NAME=moe.nb4a
VERSION_NAME=1.4.1
-PRE_VERSION_NAME=pre-1.4.1-20251026-2
+PRE_VERSION_NAME=pre-1.4.2-20260202-1
VERSION_CODE=45
From 5768494d8ae3c74a057bb6d46c0f8dc071b0d821 Mon Sep 17 00:00:00 2001
From: armv9 <48624112+arm64v8a@users.noreply.github.com>
Date: Mon, 9 Feb 2026 11:48:30 +0800
Subject: [PATCH 18/18] 1.4.2
---
nb4a.properties | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/nb4a.properties b/nb4a.properties
index fb38d97eb..b2433289a 100644
--- a/nb4a.properties
+++ b/nb4a.properties
@@ -1,4 +1,4 @@
PACKAGE_NAME=moe.nb4a
-VERSION_NAME=1.4.1
+VERSION_NAME=1.4.2
PRE_VERSION_NAME=pre-1.4.2-20260202-1
-VERSION_CODE=45
+VERSION_CODE=46