From 7475d1757b1475eff20adba5d02cb3b512808077 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Fri, 27 Feb 2026 17:44:40 +0800
Subject: [PATCH] Add detailed subscription update result
---
.../v2ray/ang/dto/SubscriptionUpdateResult.kt | 24 +++++++++
.../com/v2ray/ang/handler/AngConfigManager.kt | 49 ++++++++++++-------
.../java/com/v2ray/ang/ui/MainActivity.kt | 19 +++++--
.../com/v2ray/ang/ui/SubSettingActivity.kt | 18 ++++---
.../com/v2ray/ang/viewmodel/MainViewModel.kt | 7 +--
.../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, 101 insertions(+), 34 deletions(-)
create mode 100644 V2rayNG/app/src/main/java/com/v2ray/ang/dto/SubscriptionUpdateResult.kt
diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/SubscriptionUpdateResult.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/SubscriptionUpdateResult.kt
new file mode 100644
index 00000000..9ccf94c0
--- /dev/null
+++ b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/SubscriptionUpdateResult.kt
@@ -0,0 +1,24 @@
+package com.v2ray.ang.dto
+
+/**
+ * Result of subscription update operation
+ */
+data class SubscriptionUpdateResult(
+ val configCount: Int = 0, // Total configs updated
+ val successCount: Int = 0, // Subscriptions updated successfully
+ val failureCount: Int = 0, // Subscriptions failed to update
+ val skipCount: Int = 0 // Subscriptions skipped (disabled)
+) {
+ /**
+ * Combine two results by adding their counts
+ */
+ operator fun plus(other: SubscriptionUpdateResult): SubscriptionUpdateResult {
+ return SubscriptionUpdateResult(
+ configCount = this.configCount + other.configCount,
+ successCount = this.successCount + other.successCount,
+ failureCount = this.failureCount + other.failureCount,
+ skipCount = this.skipCount + other.skipCount
+ )
+ }
+}
+
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 ff41f884..7fdf6ba6 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
@@ -10,6 +10,7 @@ import com.v2ray.ang.R
import com.v2ray.ang.dto.ProfileItem
import com.v2ray.ang.dto.SubscriptionCache
import com.v2ray.ang.dto.SubscriptionItem
+import com.v2ray.ang.dto.SubscriptionUpdateResult
import com.v2ray.ang.enums.EConfigType
import com.v2ray.ang.extension.isNotNullEmpty
import com.v2ray.ang.fmt.CustomFmt
@@ -433,45 +434,48 @@ object AngConfigManager {
/**
* Updates the configuration via all subscriptions.
*
- * @return The number of configurations updated.
+ * @return Detailed result of the subscription update operation.
*/
- fun updateConfigViaSubAll(): Int {
- var count = 0
- try {
- MmkvManager.decodeSubscriptions().forEach {
- count += updateConfigViaSub(it)
+ fun updateConfigViaSubAll(): SubscriptionUpdateResult {
+ return try {
+ val subscriptions = MmkvManager.decodeSubscriptions()
+ subscriptions.fold(SubscriptionUpdateResult()) { acc, subscription ->
+ acc + updateConfigViaSub(subscription)
}
} catch (e: Exception) {
Log.e(AppConfig.TAG, "Failed to update config via all subscriptions", e)
- return 0
+ SubscriptionUpdateResult()
}
- return count
}
/**
* Updates the configuration via a subscription.
*
* @param it The subscription item.
- * @return The number of configurations updated.
+ * @return Subscription update result.
*/
- fun updateConfigViaSub(it: SubscriptionCache): Int {
+ fun updateConfigViaSub(it: SubscriptionCache): SubscriptionUpdateResult {
try {
+ // Check if disabled
+ if (!it.subscription.enabled) {
+ return SubscriptionUpdateResult(skipCount = 1)
+ }
+
+ // Validate subscription info
if (TextUtils.isEmpty(it.guid)
|| TextUtils.isEmpty(it.subscription.remarks)
|| TextUtils.isEmpty(it.subscription.url)
) {
- return 0
- }
- if (!it.subscription.enabled) {
- return 0
+ return SubscriptionUpdateResult(skipCount = 1)
}
+
val url = HttpUtil.toIdnUrl(it.subscription.url)
if (!Utils.isValidUrl(url)) {
- return 0
+ return SubscriptionUpdateResult(failureCount = 1)
}
if (!it.subscription.allowInsecureUrl) {
if (!Utils.isValidSubUrl(url)) {
- return 0
+ return SubscriptionUpdateResult(failureCount = 1)
}
}
Log.i(AppConfig.TAG, url)
@@ -493,18 +497,25 @@ object AngConfigManager {
}
}
if (configText.isEmpty()) {
- return 0
+ return SubscriptionUpdateResult(failureCount = 1)
}
+
val count = parseConfigViaSub(configText, it.guid, false)
if (count > 0) {
it.subscription.lastUpdated = System.currentTimeMillis()
MmkvManager.encodeSubscription(it.guid, it.subscription)
Log.i(AppConfig.TAG, "Subscription updated: ${it.subscription.remarks}, $count configs")
+ return SubscriptionUpdateResult(
+ configCount = count,
+ successCount = 1
+ )
+ } else {
+ // Got response but no valid configs parsed
+ return SubscriptionUpdateResult(failureCount = 1)
}
- return count
} catch (e: Exception) {
Log.e(AppConfig.TAG, "Failed to update config via subscription", e)
- return 0
+ return SubscriptionUpdateResult(failureCount = 1)
}
}
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 7b35b848..3f463961 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
@@ -437,14 +437,23 @@ class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelect
showLoading()
lifecycleScope.launch(Dispatchers.IO) {
- val count = mainViewModel.updateConfigViaSubAll()
+ val result = mainViewModel.updateConfigViaSubAll()
delay(500L)
launch(Dispatchers.Main) {
- if (count > 0) {
- toast(getString(R.string.title_update_config_count, count))
- mainViewModel.reloadServerList()
+ if (result.successCount + result.failureCount + result.skipCount == 0) {
+ toast(R.string.title_update_subscription_no_subscription)
+ } else if (result.successCount > 0 && result.failureCount + result.skipCount == 0) {
+ toast(getString(R.string.title_update_config_count, result.configCount))
} else {
- toastError(R.string.toast_failure)
+ toast(
+ getString(
+ R.string.title_update_subscription_result,
+ result.configCount, result.successCount, result.failureCount, result.skipCount
+ )
+ )
+ }
+ if (result.configCount > 0) {
+ mainViewModel.reloadServerList()
}
hideLoading()
}
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 cd7d178c..6f42483f 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
@@ -18,8 +18,6 @@ import com.v2ray.ang.contracts.BaseAdapterListener
import com.v2ray.ang.databinding.ActivitySubSettingBinding
import com.v2ray.ang.databinding.ItemQrcodeBinding
import com.v2ray.ang.extension.toast
-import com.v2ray.ang.extension.toastError
-import com.v2ray.ang.extension.toastSuccess
import com.v2ray.ang.handler.AngConfigManager
import com.v2ray.ang.handler.MmkvManager
import com.v2ray.ang.helper.SimpleItemTouchHelperCallback
@@ -77,14 +75,20 @@ class SubSettingActivity : BaseActivity() {
showLoading()
lifecycleScope.launch(Dispatchers.IO) {
- val count = AngConfigManager.updateConfigViaSubAll()
+ val result = AngConfigManager.updateConfigViaSubAll()
delay(500L)
launch(Dispatchers.Main) {
- if (count > 0) {
- toastSuccess(R.string.toast_success)
- refreshData()
+ if (result.successCount + result.failureCount + result.skipCount == 0) {
+ toast(R.string.title_update_subscription_no_subscription)
+ } else if (result.successCount > 0 && result.failureCount + result.skipCount == 0) {
+ toast(getString(R.string.title_update_config_count, result.configCount))
} else {
- toastError(R.string.toast_failure)
+ toast(
+ getString(
+ R.string.title_update_subscription_result,
+ result.configCount, result.successCount, result.failureCount, result.skipCount
+ )
+ )
}
hideLoading()
}
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 242dc638..80fef5ff 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
@@ -17,6 +17,7 @@ import com.v2ray.ang.R
import com.v2ray.ang.dto.GroupMapItem
import com.v2ray.ang.dto.ServersCache
import com.v2ray.ang.dto.SubscriptionCache
+import com.v2ray.ang.dto.SubscriptionUpdateResult
import com.v2ray.ang.extension.serializable
import com.v2ray.ang.extension.toastError
import com.v2ray.ang.extension.toastSuccess
@@ -183,13 +184,13 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
/**
* Updates the configuration via subscription for all servers.
- * @return The number of updated configurations.
+ * @return Detailed result of the subscription update operation.
*/
- fun updateConfigViaSubAll(): Int {
+ fun updateConfigViaSubAll(): SubscriptionUpdateResult {
if (subscriptionId.isEmpty()) {
return AngConfigManager.updateConfigViaSubAll()
} else {
- val subItem = MmkvManager.decodeSubscription(subscriptionId) ?: return 0
+ val subItem = MmkvManager.decodeSubscription(subscriptionId) ?: return SubscriptionUpdateResult()
return AngConfigManager.updateConfigViaSub(SubscriptionCache(subscriptionId, subItem))
}
}
diff --git a/V2rayNG/app/src/main/res/values-ar/strings.xml b/V2rayNG/app/src/main/res/values-ar/strings.xml
index e33e8274..99bea97a 100644
--- a/V2rayNG/app/src/main/res/values-ar/strings.xml
+++ b/V2rayNG/app/src/main/res/values-ar/strings.xml
@@ -297,6 +297,8 @@
Import %d configurations
Export %d configurations
Update %d configurations
+ Updated %1$d configs (%2$d success, %3$d failed, %4$d skipped)
+ No subscriptions
بدء الخدمة
تأكيد
diff --git a/V2rayNG/app/src/main/res/values-bn/strings.xml b/V2rayNG/app/src/main/res/values-bn/strings.xml
index ab3870c1..32dacfb3 100644
--- a/V2rayNG/app/src/main/res/values-bn/strings.xml
+++ b/V2rayNG/app/src/main/res/values-bn/strings.xml
@@ -296,6 +296,8 @@
Import %d configurations
Export %d configurations
Update %d configurations
+ Updated %1$d configs (%2$d success, %3$d failed, %4$d skipped)
+ No subscriptions
সার্ভিস শুরু করুন
নিশ্চিত করুন
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 2d79ad15..db3c3c49 100644
--- a/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml
+++ b/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml
@@ -297,6 +297,8 @@
و من ٱووردن %d کانفیگ
و در کشیڌن %d کانفیگ
ورۊ کردن %d کانفیگ
+ Updated %1$d configs (%2$d success, %3$d failed, %4$d skipped)
+ No subscriptions
ره وندن خدمات
قوۊل
diff --git a/V2rayNG/app/src/main/res/values-fa/strings.xml b/V2rayNG/app/src/main/res/values-fa/strings.xml
index 75278311..52d3b8f0 100644
--- a/V2rayNG/app/src/main/res/values-fa/strings.xml
+++ b/V2rayNG/app/src/main/res/values-fa/strings.xml
@@ -294,6 +294,8 @@
وارد کردن %d کانفیگ
صادر کردن %d کانفیگ
آپدیت کردن %d کانفیگ
+ Updated %1$d configs (%2$d success, %3$d failed, %4$d skipped)
+ No subscriptions
شروع خدمات
تایید
diff --git a/V2rayNG/app/src/main/res/values-ru/strings.xml b/V2rayNG/app/src/main/res/values-ru/strings.xml
index 90090fd5..d76681d7 100644
--- a/V2rayNG/app/src/main/res/values-ru/strings.xml
+++ b/V2rayNG/app/src/main/res/values-ru/strings.xml
@@ -295,6 +295,8 @@
Импортировано профилей: %d
Экспортировано профилей: %d
Обновлено профилей: %d
+ Updated %1$d configs (%2$d success, %3$d failed, %4$d skipped)
+ No subscriptions
Запуск службы
Подтвердить
diff --git a/V2rayNG/app/src/main/res/values-vi/strings.xml b/V2rayNG/app/src/main/res/values-vi/strings.xml
index 745d71ab..7c15e378 100644
--- a/V2rayNG/app/src/main/res/values-vi/strings.xml
+++ b/V2rayNG/app/src/main/res/values-vi/strings.xml
@@ -297,6 +297,8 @@
Import %d configurations
Export %d configurations
Update %d configurations
+ Updated %1$d configs (%2$d success, %3$d failed, %4$d skipped)
+ No subscriptions
Khởi động v2rayNG
Xác nhận
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 77e80360..90c30b95 100644
--- a/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml
+++ b/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml
@@ -295,6 +295,8 @@
导入 %d 个配置
导出 %d 个配置
更新 %d 个配置
+ 更新了 %1$d 个配置(%2$d 个成功,%3$d 个失败,%4$d 个跳过)
+ 无订阅
启动服务
确定
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 70f51cdd..79f911d1 100644
--- a/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml
+++ b/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml
@@ -295,6 +295,8 @@
匯入 %d 個配置
匯出 %d 個配置
更新 %d 個配置
+ 更新了 %1$d 個配置(%2$d 個成功,%3$d 個失敗,%4$d 個跳過)
+ 無訂閱
啟動服務
確定
diff --git a/V2rayNG/app/src/main/res/values/strings.xml b/V2rayNG/app/src/main/res/values/strings.xml
index 5e4ddc8b..35459db8 100644
--- a/V2rayNG/app/src/main/res/values/strings.xml
+++ b/V2rayNG/app/src/main/res/values/strings.xml
@@ -301,6 +301,8 @@
Import %d configs
Export %d configs
Update %d configs
+ Updated %1$d configs (%2$d success, %3$d failed, %4$d skipped)
+ No subscriptions
Start Service
Confirm