From 5932b0db29dc5d1fe53ad5151f7b31cf8811127f Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Tue, 12 May 2026 11:05:11 +0800 Subject: [PATCH] Add UrlContentRequest and refactor HttpUtil Introduce UrlContentRequest DTO to encapsulate url, timeout, httpPort, proxy credentials and userAgent. Refactor HttpUtil APIs (getUrlContent, getUrlContentWithUserAgent, downloadToFile) to accept UrlContentRequest and update callers accordingly in AngConfigManager, SpeedtestManager, UpdateCheckerManager, PerAppProxyActivity and UserAssetViewModel. This centralizes HTTP request parameters, reduces duplicated parameter lists, and preserves existing behaviors (redirect handling, userAgent fallback, proxy auth). --- .../com/v2ray/ang/dto/UrlContentRequest.kt | 10 ++++ .../com/v2ray/ang/handler/AngConfigManager.kt | 19 ++++++- .../com/v2ray/ang/handler/SpeedtestManager.kt | 11 +++- .../v2ray/ang/handler/UpdateCheckerManager.kt | 18 ++++++- .../com/v2ray/ang/ui/PerAppProxyActivity.kt | 18 ++++++- .../main/java/com/v2ray/ang/util/HttpUtil.kt | 52 +++++++------------ .../v2ray/ang/viewmodel/UserAssetViewModel.kt | 14 ++++- 7 files changed, 101 insertions(+), 41 deletions(-) create mode 100644 V2rayNG/app/src/main/java/com/v2ray/ang/dto/UrlContentRequest.kt diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/UrlContentRequest.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/UrlContentRequest.kt new file mode 100644 index 000000000..03fbaa314 --- /dev/null +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/UrlContentRequest.kt @@ -0,0 +1,10 @@ +package com.v2ray.ang.dto + +data class UrlContentRequest( + val url: String?, + val timeout: Int = 15000, + val httpPort: Int = 0, + val proxyUsername: String? = null, + val proxyPassword: String? = null, + val userAgent: String? = null +) \ No newline at end of file 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 14d863264..fdcfaf52d 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 @@ -11,6 +11,7 @@ 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.dto.UrlContentRequest import com.v2ray.ang.enums.EConfigType import com.v2ray.ang.extension.isNotNullEmpty import com.v2ray.ang.fmt.CustomFmt @@ -535,14 +536,28 @@ object AngConfigManager { var configText = try { val httpPort = SettingsManager.getHttpPort() - HttpUtil.getUrlContentWithUserAgent(url, userAgent, 15000, httpPort, proxyUsername, proxyPassword) + HttpUtil.getUrlContentWithUserAgent( + UrlContentRequest( + url = url, + userAgent = userAgent, + timeout = 15000, + httpPort = httpPort, + proxyUsername = proxyUsername, + proxyPassword = proxyPassword + ) + ) } catch (e: Exception) { LogUtil.e(AppConfig.ANG_PACKAGE, "Update subscription: proxy not ready or other error", e) "" } if (configText.isEmpty()) { configText = try { - HttpUtil.getUrlContentWithUserAgent(url, userAgent) + HttpUtil.getUrlContentWithUserAgent( + UrlContentRequest( + url = url, + userAgent = userAgent + ) + ) } catch (e: Exception) { LogUtil.e(AppConfig.TAG, "Update subscription: Failed to get URL content with user agent", e) "" 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 35f567d60..8cd9467e0 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 @@ -2,6 +2,7 @@ package com.v2ray.ang.handler import com.v2ray.ang.AppConfig import com.v2ray.ang.dto.IPAPIInfo +import com.v2ray.ang.dto.UrlContentRequest import com.v2ray.ang.util.HttpUtil import com.v2ray.ang.util.JsonUtil import com.v2ray.ang.util.LogUtil @@ -88,7 +89,15 @@ object SpeedtestManager { val proxyPassword = SettingsManager.getSocksPassword() val httpPort = SettingsManager.getHttpPort() if (httpPort == 0) return null - val content = HttpUtil.getUrlContent(url, 5000, httpPort, proxyUsername, proxyPassword) ?: return null + val content = HttpUtil.getUrlContent( + UrlContentRequest( + url = url, + timeout = 5000, + httpPort = httpPort, + proxyUsername = proxyUsername, + proxyPassword = proxyPassword + ) + ) ?: return null val ipInfo = JsonUtil.fromJson(content, IPAPIInfo::class.java) ?: return null val ip = listOf( diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/UpdateCheckerManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/UpdateCheckerManager.kt index caa872567..f498eb28f 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/UpdateCheckerManager.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/UpdateCheckerManager.kt @@ -5,6 +5,7 @@ import com.v2ray.ang.AppConfig import com.v2ray.ang.BuildConfig import com.v2ray.ang.dto.CheckUpdateResult import com.v2ray.ang.dto.GitHubRelease +import com.v2ray.ang.dto.UrlContentRequest import com.v2ray.ang.extension.concatUrl import com.v2ray.ang.util.HttpUtil import com.v2ray.ang.util.JsonUtil @@ -23,10 +24,23 @@ object UpdateCheckerManager { val proxyUsername = SettingsManager.getSocksUsername() val proxyPassword = SettingsManager.getSocksPassword() - var response = HttpUtil.getUrlContent(url, 5000) + var response = HttpUtil.getUrlContent( + UrlContentRequest( + url = url, + timeout = 5000 + ) + ) if (response.isNullOrEmpty()) { val httpPort = SettingsManager.getHttpPort() - response = HttpUtil.getUrlContent(url, 5000, httpPort, proxyUsername, proxyPassword) + response = HttpUtil.getUrlContent( + UrlContentRequest( + url = url, + timeout = 5000, + httpPort = httpPort, + proxyUsername = proxyUsername, + proxyPassword = proxyPassword + ) + ) ?: throw IllegalStateException("Failed to get response") } 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 460de6b48..9a8e2206d 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 @@ -14,6 +14,7 @@ import com.v2ray.ang.AppConfig.ANG_PACKAGE import com.v2ray.ang.R import com.v2ray.ang.databinding.ActivityBypassListBinding import com.v2ray.ang.dto.AppInfo +import com.v2ray.ang.dto.UrlContentRequest import com.v2ray.ang.extension.toast import com.v2ray.ang.extension.toastSuccess import com.v2ray.ang.extension.v2RayApplication @@ -189,12 +190,25 @@ class PerAppProxyActivity : BaseActivity() { val url = AppConfig.ANDROID_PACKAGE_NAME_LIST_URL lifecycleScope.launch(Dispatchers.IO) { - var content = HttpUtil.getUrlContent(url, 5000) + var content = HttpUtil.getUrlContent( + UrlContentRequest( + url = url, + timeout = 5000 + ) + ) if (content.isNullOrEmpty()) { val proxyUsername = SettingsManager.getSocksUsername() val proxyPassword = SettingsManager.getSocksPassword() val httpPort = SettingsManager.getHttpPort() - content = HttpUtil.getUrlContent(url, 5000, httpPort, proxyUsername, proxyPassword) ?: "" + content = HttpUtil.getUrlContent( + UrlContentRequest( + url = url, + timeout = 5000, + httpPort = httpPort, + proxyUsername = proxyUsername, + proxyPassword = proxyPassword + ) + ) ?: "" } launch(Dispatchers.Main) { //LogUtil.i(AppConfig.TAG, content) 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 c693253cf..4282801d2 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 @@ -3,6 +3,7 @@ package com.v2ray.ang.util import com.v2ray.ang.AppConfig import com.v2ray.ang.AppConfig.LOOPBACK import com.v2ray.ang.BuildConfig +import com.v2ray.ang.dto.UrlContentRequest import okhttp3.Credentials import okhttp3.OkHttpClient import okhttp3.Request @@ -108,20 +109,15 @@ object HttpUtil { * @param httpPort The HTTP port to use. * @return The content of the URL as a string. */ - fun getUrlContent( - url: String, - timeout: Int, - httpPort: Int = 0, - proxyUsername: String? = null, - proxyPassword: String? = null - ): String? { - val client = buildOkHttpClient(timeout, httpPort, proxyUsername, proxyPassword, followRedirects = true) + fun getUrlContent(request: UrlContentRequest): String? { + val url = request.url ?: return null + val client = buildOkHttpClient(request.timeout, request.httpPort, request.proxyUsername, request.proxyPassword, followRedirects = true) val requestBuilder = Request.Builder() .url(url) .get() .header("Connection", "close") - if (httpPort != 0 && !proxyUsername.isNullOrBlank() && !proxyPassword.isNullOrBlank()) { - requestBuilder.header("Proxy-Authorization", Credentials.basic(proxyUsername, proxyPassword)) + if (request.httpPort != 0 && !request.proxyUsername.isNullOrBlank() && !request.proxyPassword.isNullOrBlank()) { + requestBuilder.header("Proxy-Authorization", Credentials.basic(request.proxyUsername, request.proxyPassword)) } try { client.newCall(requestBuilder.build()).execute().use { response -> @@ -147,25 +143,18 @@ object HttpUtil { * @throws IOException If an I/O error occurs. */ @Throws(IOException::class) - fun getUrlContentWithUserAgent( - url: String?, - userAgent: String?, - timeout: Int = 15000, - httpPort: Int = 0, - proxyUsername: String? = null, - proxyPassword: String? = null - ): String { - var currentUrl = url + fun getUrlContentWithUserAgent(request: UrlContentRequest): String { + var currentUrl = request.url var redirects = 0 val maxRedirects = 3 while (redirects++ < maxRedirects) { if (currentUrl == null) continue - val client = buildOkHttpClient(timeout, httpPort, proxyUsername, proxyPassword, followRedirects = false) - val finalUserAgent = if (userAgent.isNullOrBlank()) { + val client = buildOkHttpClient(request.timeout, request.httpPort, request.proxyUsername, request.proxyPassword, followRedirects = false) + val finalUserAgent = if (request.userAgent.isNullOrBlank()) { "v2rayNG/${BuildConfig.VERSION_NAME}" } else { - userAgent + request.userAgent } val requestBuilder = Request.Builder() .url(currentUrl) @@ -175,8 +164,8 @@ object HttpUtil { applyEmbeddedBasicAuthHeader(currentUrl, requestBuilder) - if (httpPort != 0 && !proxyUsername.isNullOrBlank() && !proxyPassword.isNullOrBlank()) { - requestBuilder.header("Proxy-Authorization", Credentials.basic(proxyUsername, proxyPassword)) + if (request.httpPort != 0 && !request.proxyUsername.isNullOrBlank() && !request.proxyPassword.isNullOrBlank()) { + requestBuilder.header("Proxy-Authorization", Credentials.basic(request.proxyUsername, request.proxyPassword)) } client.newCall(requestBuilder.build()).execute().use { response -> @@ -271,20 +260,17 @@ object HttpUtil { } fun downloadToFile( - url: String, - targetFile: File, - timeout: Int = 15000, - httpPort: Int = 0, - proxyUsername: String? = null, - proxyPassword: String? = null + request: UrlContentRequest, + targetFile: File ): Boolean { - val client = buildOkHttpClient(timeout, httpPort, proxyUsername, proxyPassword, followRedirects = true) + val url = request.url ?: return false + val client = buildOkHttpClient(request.timeout, request.httpPort, request.proxyUsername, request.proxyPassword, followRedirects = true) val requestBuilder = Request.Builder() .url(url) .get() .header("Connection", "close") - if (httpPort != 0 && !proxyUsername.isNullOrBlank() && !proxyPassword.isNullOrBlank()) { - requestBuilder.header("Proxy-Authorization", Credentials.basic(proxyUsername, proxyPassword)) + if (request.httpPort != 0 && !request.proxyUsername.isNullOrBlank() && !request.proxyPassword.isNullOrBlank()) { + requestBuilder.header("Proxy-Authorization", Credentials.basic(request.proxyUsername, request.proxyPassword)) } return try { diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/UserAssetViewModel.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/UserAssetViewModel.kt index 3799cd522..6cef5f98b 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/UserAssetViewModel.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/UserAssetViewModel.kt @@ -4,6 +4,7 @@ import androidx.lifecycle.ViewModel import com.v2ray.ang.AppConfig import com.v2ray.ang.dto.AssetUrlCache import com.v2ray.ang.dto.AssetUrlItem +import com.v2ray.ang.dto.UrlContentRequest import com.v2ray.ang.extension.concatUrl import com.v2ray.ang.handler.MmkvManager import com.v2ray.ang.util.HttpUtil @@ -92,7 +93,18 @@ class UserAssetViewModel : ViewModel() { val targetTemp = File(extDir, item.remarks + "_temp") val target = File(extDir, item.remarks) try { - if (HttpUtil.downloadToFile(item.url, targetTemp, 15000, httpPort, proxyUsername, proxyPassword)) { + if ( + HttpUtil.downloadToFile( + UrlContentRequest( + url = item.url, + timeout = 15000, + httpPort = httpPort, + proxyUsername = proxyUsername, + proxyPassword = proxyPassword + ), + targetTemp + ) + ) { targetTemp.renameTo(target) return true }