Add dynamic local SOCKS port option

Introduce a dynamic SOCKS port feature: add PREF_DYNAMIC_SOCKS_PORT key, settings UI checkbox and localized strings, and pref XML entry. SettingsManager now holds a runtimeSocksPort, can generate and refresh a random port (generateRandomSocksPort, refreshRuntimeSocksPort) and respects the dynamic setting when returning getSocksPort(). V2Ray service startup triggers a runtime port refresh. Settings UI disables manual SOCKS port input when dynamic mode is enabled. Minor i18n additions (QR code/title) included for several locales.
This commit is contained in:
2dust committed 2026-04-14 14:42:31 +08:00
1 parent e54e713db7
commit d7abb8f49e
14 files changed
+84 -1

No files matched your search

@@ -58,6 +58,7 @@ object AppConfig {
const val PREF_PROXY_SHARING = "pref_proxy_sharing_enabled"
const val PREF_ALLOW_INSECURE = "pref_allow_insecure"
const val PREF_SOCKS_PORT = "pref_socks_port"
const val PREF_DYNAMIC_SOCKS_PORT = "pref_dynamic_socks_port"
const val PREF_SOCKS_USERNAME = "pref_socks_username"
const val PREF_SOCKS_PASSWORD = "pref_socks_password"
const val PREF_REMOTE_DNS = "pref_remote_dns"
@@ -32,9 +32,13 @@ import java.io.File
import java.io.FileOutputStream
import java.util.Collections
import java.util.Locale
import kotlin.random.Random
object SettingsManager {
@Volatile
private var runtimeSocksPort: Int? = null
fun initApp(context: Context) {
ensureDefaultSettings()
//ensureDefaultSubscription()
@@ -273,7 +277,22 @@ object SettingsManager {
* @return The SOCKS port.
*/
fun getSocksPort(): Int {
return Utils.parseInt(MmkvManager.decodeSettingsString(AppConfig.PREF_SOCKS_PORT), AppConfig.PORT_SOCKS.toInt())
val port =
if (IsDynamicSocksPort()) {
runtimeSocksPort ?: refreshRuntimeSocksPort()
} else {
Utils.parseInt(MmkvManager.decodeSettingsString(AppConfig.PREF_SOCKS_PORT), AppConfig.PORT_SOCKS.toInt())
}
return port ?: AppConfig.PORT_SOCKS.toInt()
}
@Synchronized
fun refreshRuntimeSocksPort(): Int? {
if (IsDynamicSocksPort()) {
runtimeSocksPort = generateRandomSocksPort()
return runtimeSocksPort
}
return null
}
fun getSocksUsername(): String? {
@@ -292,6 +311,18 @@ object SettingsManager {
return getSocksPort() + if (Utils.isXray()) 0 else 1
}
private fun IsDynamicSocksPort(): Boolean {
return MmkvManager.decodeSettingsBool(AppConfig.PREF_DYNAMIC_SOCKS_PORT, false)
}
private fun generateRandomSocksPort(): Int {
return if (Utils.isXray()) {
Random.nextInt(10000, 65536)
} else {
Random.nextInt(10000, 65535)
}
}
/**
* Initialize assets.
* @param context The application context.
@@ -173,6 +173,8 @@ object V2RayServiceManager {
}
Log.i(AppConfig.TAG, "StartCore-Manager: Starting core loop for ${config.remarks}")
// refresh socks port when enabled dynamic socks port
SettingsManager.refreshRuntimeSocksPort()
val result = V2rayConfigManager.getV2rayConfig(service, guid)
if (!result.status) {
Log.e(AppConfig.TAG, "StartCore-Manager: Failed to get V2Ray config")
@@ -51,6 +51,8 @@ class SettingsActivity : BaseActivity() {
private val autoUpdateCheck by lazy { findPreference<CheckBoxPreference>(AppConfig.SUBSCRIPTION_AUTO_UPDATE) }
private val autoUpdateInterval by lazy { findPreference<EditTextPreference>(AppConfig.SUBSCRIPTION_AUTO_UPDATE_INTERVAL) }
private val mode by lazy { findPreference<ListPreference>(AppConfig.PREF_MODE) }
private val socksPort by lazy { findPreference<EditTextPreference>(AppConfig.PREF_SOCKS_PORT) }
private val dynamicSocksPort by lazy { findPreference<CheckBoxPreference>(AppConfig.PREF_DYNAMIC_SOCKS_PORT) }
private val hevTunLogLevel by lazy { findPreference<ListPreference>(AppConfig.PREF_HEV_TUNNEL_LOGLEVEL) }
private val hevTunRwTimeout by lazy { findPreference<EditTextPreference>(AppConfig.PREF_HEV_TUNNEL_RW_TIMEOUT) }
@@ -112,6 +114,11 @@ class SettingsActivity : BaseActivity() {
updateHevTunSettings(newValue as Boolean)
true
}
dynamicSocksPort?.setOnPreferenceChangeListener { _, newValue ->
updateDynamicSocksPort(newValue as Boolean)
true
}
}
private fun initPreferenceSummaries() {
@@ -175,6 +182,8 @@ class SettingsActivity : BaseActivity() {
// Initialize auto-update interval state
autoUpdateInterval?.isEnabled = MmkvManager.decodeSettingsBool(AppConfig.SUBSCRIPTION_AUTO_UPDATE, false)
updateDynamicSocksPort(MmkvManager.decodeSettingsBool(AppConfig.PREF_DYNAMIC_SOCKS_PORT, false))
}
private fun updateMode(value: String?) {
@@ -266,6 +275,10 @@ class SettingsActivity : BaseActivity() {
fragmentInterval?.isEnabled = enabled
}
private fun updateDynamicSocksPort(enabled: Boolean) {
socksPort?.isEnabled = !enabled
}
private fun updateHevTunSettings(enabled: Boolean) {
hevTunLogLevel?.isEnabled = enabled
hevTunRwTimeout?.isEnabled = enabled
@@ -219,6 +219,8 @@
<string name="title_pref_socks_port">منفذ بروكسي Local</string>
<string name="summary_pref_socks_port">منفذ بروكسي Local</string>
<string name="title_pref_dynamic_socks_port">Enable dynamic local proxy port</string>
<string name="summary_pref_dynamic_socks_port">Generate a random local proxy port each time the inbound is created</string>
<string name="title_pref_socks_username">Local proxy username (optional)</string>
<string name="summary_pref_socks_username">Username</string>
<string name="title_pref_socks_password">Local proxy password (optional)</string>
@@ -247,6 +249,7 @@
<string name="summary_pref_tg_group">الانضمام إلى مجموعة Telegram</string>
<string name="toast_tg_app_not_found">لم يتم العثور على تطبيق Telegram</string>
<string name="title_privacy_policy">سياسة الخصوصية</string>
<string name="title_qr_code">QR code</string>
<string name="title_about">حول\nترجمة م. ابراهيم قاسم</string>
<string name="title_source_code">الكود المصدري</string>
<string name="title_oss_license">Open Source licenses</string>
@@ -219,6 +219,8 @@
<string name="title_pref_socks_port">Local প্রক্সি পোর্ট</string>
<string name="summary_pref_socks_port">Local প্রক্সি পোর্ট</string>
<string name="title_pref_dynamic_socks_port">Enable dynamic local proxy port</string>
<string name="summary_pref_dynamic_socks_port">Generate a random local proxy port each time the inbound is created</string>
<string name="title_pref_socks_username">Local proxy username (optional)</string>
<string name="summary_pref_socks_username">Username</string>
<string name="title_pref_socks_password">Local proxy password (optional)</string>
@@ -247,6 +249,7 @@
<string name="summary_pref_tg_group">টেলিগ্রাম গ্রুপে যোগদান করুন</string>
<string name="toast_tg_app_not_found">টেলিগ্রাম অ্যাপ পাওয়া যায়নি</string>
<string name="title_privacy_policy">গোপনীয়তা নীতি</string>
<string name="title_qr_code">QR code</string>
<string name="title_about">সম্পর্কিত</string>
<string name="title_source_code">সোর্স কোড</string>
<string name="title_oss_license">Open Source licenses</string>
@@ -219,6 +219,12 @@
<string name="title_pref_socks_port">پورت پروکسی مهلی</string>
<string name="summary_pref_socks_port">پورت پروکسی مهلی</string>
<string name="title_pref_dynamic_socks_port">Enable dynamic local proxy port</string>
<string name="summary_pref_dynamic_socks_port">Generate a random local proxy port each time the inbound is created</string>
<string name="title_pref_socks_username">Local proxy username (optional)</string>
<string name="summary_pref_socks_username">Username</string>
<string name="title_pref_socks_password">Local proxy password (optional)</string>
<string name="summary_pref_socks_password">Password</string>
<string name="title_pref_local_dns_port">پورت DNS مهلی</string>
<string name="summary_pref_local_dns_port">پورت DNS مهلی</string>
@@ -243,6 +249,7 @@
<string name="summary_pref_tg_group">ٱووڌن من بونکۊ تلگرام</string>
<string name="toast_tg_app_not_found">برنومه تلگرامن نجوست</string>
<string name="title_privacy_policy">هریم سیخومی</string>
<string name="title_qr_code">QR code</string>
<string name="title_about">زبار</string>
<string name="title_source_code">کود بونچک</string>
<string name="title_oss_license">موجوزا کود بونچک</string>
@@ -217,6 +217,8 @@
<string name="title_pref_socks_port">پورت پروکسی محلی</string>
<string name="summary_pref_socks_port">پورت پروکسی محلی</string>
<string name="title_pref_dynamic_socks_port">Enable dynamic local proxy port</string>
<string name="summary_pref_dynamic_socks_port">Generate a random local proxy port each time the inbound is created</string>
<string name="title_pref_socks_username">Local proxy username (optional)</string>
<string name="summary_pref_socks_username">Username</string>
<string name="title_pref_socks_password">Local proxy password (optional)</string>
@@ -246,6 +248,7 @@
<string name="toast_tg_app_not_found">برنامه تلگرام پیدا نشد</string>
<string name="title_privacy_policy">حریم خصوصی</string>
<string name="title_qr_code">QR code</string>
<string name="title_about">درباره</string>
<string name="title_source_code">کد منبع</string>
<string name="title_oss_license">مجوز های منبع باز</string>
@@ -218,6 +218,8 @@
<string name="title_pref_socks_port">Порт локального прокси</string>
<string name="summary_pref_socks_port">Порт локального прокси</string>
<string name="title_pref_dynamic_socks_port">Enable dynamic local proxy port</string>
<string name="summary_pref_dynamic_socks_port">Generate a random local proxy port each time the inbound is created</string>
<string name="title_pref_socks_username">Имя пользователя локального прокси (опционально)</string>
<string name="summary_pref_socks_username">Имя пользователя</string>
<string name="title_pref_socks_password">Пароль локального прокси (опционально)</string>
@@ -246,6 +248,7 @@
<string name="summary_pref_tg_group">Присоединиться к группе в Telegram</string>
<string name="toast_tg_app_not_found">Приложение Telegram не найдено</string>
<string name="title_privacy_policy">Политика конфиденциальности</string>
<string name="title_qr_code">QR-код</string>
<string name="title_about">О приложении</string>
<string name="title_source_code">Исходный код</string>
<string name="title_oss_license">Лицензии открытого исходного кода</string>
@@ -219,6 +219,8 @@
<string name="title_pref_socks_port">Cổng Proxy Local</string>
<string name="summary_pref_socks_port">Cổng Proxy Local</string>
<string name="title_pref_dynamic_socks_port">Enable dynamic local proxy port</string>
<string name="summary_pref_dynamic_socks_port">Generate a random local proxy port each time the inbound is created</string>
<string name="title_pref_socks_username">Tên người dùng Proxy Local (Không bắt buộc)</string>
<string name="summary_pref_socks_username">Tên người dùng</string>
<string name="title_pref_socks_password">Mật khẩu Proxy Local (Không bắt buộc)</string>
@@ -248,6 +250,7 @@
<string name="toast_tg_app_not_found">Không tìm thấy ứng dụng Telegram!</string>
<string name="title_privacy_policy">Chính sách bảo mật</string>
<string name="title_qr_code">Mã QR</string>
<string name="title_about">Giới thiệu</string>
<string name="title_source_code">Mã nguồn</string>
<string name="title_oss_license">Open Source licenses</string>
@@ -216,6 +216,8 @@
<string name="title_pref_socks_port">本地代理端口</string>
<string name="summary_pref_socks_port">本地代理端口</string>
<string name="title_pref_dynamic_socks_port">启用动态本地代理端口</string>
<string name="summary_pref_dynamic_socks_port">每次生成 inbound 时随机生成一个本地代理端口</string>
<string name="title_pref_socks_username">本地代理用户名 (可选)</string>
<string name="summary_pref_socks_username">用户名</string>
<string name="title_pref_socks_password">本地代理密码 (可选)</string>
@@ -245,6 +247,7 @@
<string name="summary_pref_tg_group">加入 Telegram Group</string>
<string name="toast_tg_app_not_found">未找到 Telegram app</string>
<string name="title_privacy_policy">隐私权政策</string>
<string name="title_qr_code">二维码</string>
<string name="title_about">关于</string>
<string name="title_source_code">源代码</string>
<string name="title_oss_license">Open Source licenses</string>
@@ -217,6 +217,8 @@
<string name="title_pref_socks_port">本地代理埠</string>
<string name="summary_pref_socks_port">本地代理埠</string>
<string name="title_pref_dynamic_socks_port">啟用動態本地代理埠</string>
<string name="summary_pref_dynamic_socks_port">每次產生 inbound 時隨機產生一個本地代理埠</string>
<string name="title_pref_socks_username">本地代理使用者名稱 (選擇)</string>
<string name="summary_pref_socks_username">使用者名稱</string>
<string name="title_pref_socks_password">本地代理密碼 (選擇)</string>
@@ -246,6 +248,7 @@
<string name="summary_pref_tg_group">加入 Telegram 群組</string>
<string name="toast_tg_app_not_found">未找到 Telegram 應用程式</string>
<string name="title_privacy_policy">隱私權政策</string>
<string name="title_qr_code">QR 碼</string>
<string name="title_about">關於</string>
<string name="title_source_code">原始碼</string>
<string name="title_oss_license">Open Source licenses</string>
@@ -222,6 +222,8 @@
<string name="title_pref_socks_port">Local proxy port</string>
<string name="summary_pref_socks_port">Local proxy port</string>
<string name="title_pref_dynamic_socks_port">Enable dynamic local proxy port</string>
<string name="summary_pref_dynamic_socks_port">Generate a random local proxy port each time the inbound is created</string>
<string name="title_pref_socks_username">Local proxy username (optional)</string>
<string name="summary_pref_socks_username">Username</string>
<string name="title_pref_socks_password">Local proxy password (optional)</string>
@@ -157,6 +157,12 @@
android:summary="10808"
android:title="@string/title_pref_socks_port" />
<CheckBoxPreference
android:defaultValue="false"
android:key="pref_dynamic_socks_port"
android:summary="@string/summary_pref_dynamic_socks_port"
android:title="@string/title_pref_dynamic_socks_port" />
<EditTextPreference
android:key="pref_socks_username"
android:summary="@string/summary_pref_socks_username"