Clarify boot auto-connect and link system VPN settings (#6179)
* Clarify device-boot connection and link system VPN settings * Match Bakhtiari system spelling in VPN settings strings
This commit is contained in:
1 parent
f4f992eb9b
commit
5bfcdf90ec
12 files changed
+145
-21
No files matched your search
@@ -1,7 +1,10 @@
|
||||
package com.v2ray.ang.ui.settings
|
||||
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
@@ -24,10 +27,14 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringArrayResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import com.v2ray.ang.AppConfig
|
||||
import com.v2ray.ang.AppConfig.VPN
|
||||
import com.v2ray.ang.R
|
||||
import com.v2ray.ang.extension.toastError
|
||||
import com.v2ray.ang.handler.AppLocaleManager
|
||||
import com.v2ray.ang.handler.MmkvManager
|
||||
import com.v2ray.ang.handler.MmkvManager.rememberMmkvBool
|
||||
@@ -44,7 +51,9 @@ import com.v2ray.ang.ui.compose.SettingsMenuItem
|
||||
import com.v2ray.ang.ui.compose.SettingsSwitchItem
|
||||
import com.v2ray.ang.ui.compose.ThemeManager
|
||||
import com.v2ray.ang.ui.compose.verticalScrollbar
|
||||
import com.v2ray.ang.util.LogUtil
|
||||
import com.v2ray.ang.util.Utils
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class SettingsActivity : BaseComponentActivity() {
|
||||
|
||||
@@ -52,6 +61,26 @@ class SettingsActivity : BaseComponentActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||
viewModel.refreshSystemVpnSettingsAvailability()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun openSystemVpnSettings() {
|
||||
try {
|
||||
startActivity(Intent(Settings.ACTION_VPN_SETTINGS))
|
||||
} catch (error: ActivityNotFoundException) {
|
||||
reportSystemVpnSettingsFailure(error)
|
||||
} catch (error: SecurityException) {
|
||||
reportSystemVpnSettingsFailure(error)
|
||||
}
|
||||
}
|
||||
|
||||
private fun reportSystemVpnSettingsFailure(error: RuntimeException) {
|
||||
LogUtil.e(AppConfig.TAG, "Cannot open system VPN settings", error)
|
||||
toastError(R.string.toast_system_vpn_settings_unavailable)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -59,7 +88,8 @@ class SettingsActivity : BaseComponentActivity() {
|
||||
SettingsScreen(
|
||||
viewModel = viewModel,
|
||||
onBackClick = { finish() },
|
||||
onModeHelpClicked = { Utils.openUri(this, AppConfig.APP_WIKI_MODE) }
|
||||
onModeHelpClicked = { Utils.openUri(this, AppConfig.APP_WIKI_MODE) },
|
||||
onSystemVpnSettingsClicked = ::openSystemVpnSettings
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -69,10 +99,12 @@ class SettingsActivity : BaseComponentActivity() {
|
||||
fun SettingsScreen(
|
||||
viewModel: SettingsViewModel,
|
||||
onBackClick: () -> Unit,
|
||||
onModeHelpClicked: () -> Unit
|
||||
onModeHelpClicked: () -> Unit,
|
||||
onSystemVpnSettingsClicked: () -> Unit
|
||||
) {
|
||||
val scrollState = rememberScrollState()
|
||||
val isLoading by viewModel.isLoading.collectAsStateWithLifecycle()
|
||||
val systemVpnSettingsAvailable by viewModel.systemVpnSettingsAvailable.collectAsStateWithLifecycle()
|
||||
var uiSettingsExpanded by rememberSaveable { mutableStateOf(true) }
|
||||
var vpnSettingsExpanded by rememberSaveable { mutableStateOf(true) }
|
||||
var coreSettingsExpanded by rememberSaveable { mutableStateOf(true) }
|
||||
@@ -612,6 +644,13 @@ fun SettingsScreen(
|
||||
checked = isBooted,
|
||||
onCheckedChange = { isBooted = it }
|
||||
)
|
||||
if (systemVpnSettingsAvailable) {
|
||||
SettingsMenuItem(
|
||||
title = stringResource(R.string.title_system_vpn_settings),
|
||||
subtitle = stringResource(R.string.summary_system_vpn_settings),
|
||||
onClick = onSystemVpnSettingsClicked
|
||||
)
|
||||
}
|
||||
SettingsEditItem(
|
||||
title = stringResource(R.string.title_pref_delay_test_url),
|
||||
value = delayTestUrl,
|
||||
|
||||
@@ -1,15 +1,29 @@
|
||||
package com.v2ray.ang.ui.settings
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Intent
|
||||
import android.provider.Settings
|
||||
import com.v2ray.ang.AppConfig
|
||||
import com.v2ray.ang.R
|
||||
import com.v2ray.ang.root.RootManager
|
||||
import com.v2ray.ang.ui.base.BaseViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class SettingsViewModel(application: Application) : BaseViewModel(application) {
|
||||
|
||||
private val _systemVpnSettingsAvailable = MutableStateFlow(false)
|
||||
val systemVpnSettingsAvailable = _systemVpnSettingsAvailable.asStateFlow()
|
||||
|
||||
suspend fun refreshSystemVpnSettingsAvailability() {
|
||||
_systemVpnSettingsAvailable.value = withContext(Dispatchers.IO) {
|
||||
// Android exposes the VPN page, not a direct link to the Always-on VPN switch.
|
||||
Intent(Settings.ACTION_VPN_SETTINGS).resolveActivity(getApplication<Application>().packageManager) != null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for root access and requests it if necessary.
|
||||
* Updates [isLoading] during the process.
|
||||
@@ -56,4 +70,4 @@ class SettingsViewModel(application: Application) : BaseViewModel(application) {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -172,8 +172,11 @@
|
||||
<string name="title_vpn_settings">إعدادات VPN</string>
|
||||
<string name="title_pref_per_app_proxy">الوكيل لكل تطبيق</string>
|
||||
<string name="summary_pref_per_app_proxy">عام: تستخدم التطبيقات المحددة الوكيل، وتتصل التطبيقات غير المحددة مباشرة؛\nوضع التجاوز: تتصل التطبيقات المحددة مباشرة، وتستخدم التطبيقات غير المحددة الوكيل.\nيتوفر في القائمة خيار لتحديد التطبيقات التي ستستخدم الوكيل تلقائيًا.</string>
|
||||
<string name="title_pref_is_booted">الاتصال تلقائياً عند بدء التشغيل</string>
|
||||
<string name="summary_pref_is_booted">الاتصال تلقائياً بالخادم المحدد عند بدء التشغيل؛ قد لا تنجح المحاولة</string>
|
||||
<string name="title_pref_is_booted">الاتصال بعد إقلاع الجهاز</string>
|
||||
<string name="summary_pref_is_booted">الاتصال تلقائياً بالخادم المحدد بعد إقلاع الجهاز. قد يقيّد Android بدء التشغيل في الخلفية.</string>
|
||||
<string name="title_system_vpn_settings">إعدادات VPN للنظام</string>
|
||||
<string name="summary_system_vpn_settings">فتح إعدادات VPN في Android لإعداد VPN الدائم، إذا كان الجهاز يدعمه.</string>
|
||||
<string name="toast_system_vpn_settings_unavailable">إعدادات VPN للنظام غير متاحة.</string>
|
||||
|
||||
|
||||
<string name="subscription_updater_job_tips">ستعمل المهمة في الخلفية، وسيُعرض تقدمها في الإشعار.</string>
|
||||
|
||||
@@ -172,8 +172,11 @@
|
||||
<string name="title_vpn_settings">VPN সেটিংস</string>
|
||||
<string name="title_pref_per_app_proxy">প্রতি-অ্যাপ প্রক্সি</string>
|
||||
<string name="summary_pref_per_app_proxy">সাধারণ: নির্বাচিত অ্যাপ প্রক্সি ব্যবহার করে, অনির্বাচিত অ্যাপ সরাসরি সংযুক্ত হয়;\nবাইপাস মোড: নির্বাচিত অ্যাপ সরাসরি সংযুক্ত হয়, অনির্বাচিত অ্যাপ প্রক্সি ব্যবহার করে।\nযেসব অ্যাপ প্রক্সি ব্যবহার করবে, সেগুলি স্বয়ংক্রিয়ভাবে নির্বাচনের বিকল্পটি মেনুতে আছে।</string>
|
||||
<string name="title_pref_is_booted">চালুর সময় স্বয়ংক্রিয় সংযোগ</string>
|
||||
<string name="summary_pref_is_booted">চালুর সময় নির্বাচিত সার্ভারে স্বয়ংক্রিয়ভাবে সংযোগ করে; সংযোগ ব্যর্থ হতে পারে</string>
|
||||
<string name="title_pref_is_booted">ডিভাইস চালু হওয়ার পর সংযোগ</string>
|
||||
<string name="summary_pref_is_booted">ডিভাইস চালু হওয়ার পর নির্বাচিত সার্ভারে স্বয়ংক্রিয়ভাবে সংযোগ করুন। Android ব্যাকগ্রাউন্ডে চালু হওয়া সীমিত করতে পারে।</string>
|
||||
<string name="title_system_vpn_settings">সিস্টেমের VPN সেটিংস</string>
|
||||
<string name="summary_system_vpn_settings">ডিভাইসে সমর্থিত হলে সর্বদা চালু VPN কনফিগার করতে Android-এর VPN সেটিংস খুলুন।</string>
|
||||
<string name="toast_system_vpn_settings_unavailable">সিস্টেমের VPN সেটিংস উপলভ্য নয়।</string>
|
||||
|
||||
|
||||
<string name="subscription_updater_job_tips">কাজটি ব্যাকগ্রাউন্ডে চলবে এবং অগ্রগতির তথ্য বিজ্ঞপ্তিতে দেখানো হবে।</string>
|
||||
|
||||
@@ -172,8 +172,11 @@
|
||||
<string name="title_vpn_settings">سامووا VPN</string>
|
||||
<string name="title_pref_per_app_proxy">پروکسی و ری برنومه</string>
|
||||
<string name="summary_pref_per_app_proxy">پوی وولاتی: برنومه پسند وابیڌه وا ی پروکسی منپیز ابۊ، برنومه پسند نوابیڌه موستقیمن منپیز ابۊ.\nحالت دور زیڌن: برنومه پسند وابیڌه موستقیمن منپیز ابۊ، برنومه پسند نوابیڌه وا ی پروکسی منپیز ابۊ.\nپسند خوتکار برنومه یلی ک وا ی پروکسی منپیز ابۊن من نومگه امکووݩ پذیر هڌ.</string>
|
||||
<string name="title_pref_is_booted">منپیز خوتکار مجال ره ونی</string>
|
||||
<string name="summary_pref_is_booted">مجال ره وندن، خوساخوس و سرور پسند بیڌه منپیز ابۊ که گاشڌ نا مووفق بۊ.</string>
|
||||
<string name="title_pref_is_booted">منپیز بئڌ ره وندن دسگا</string>
|
||||
<string name="summary_pref_is_booted">بئڌ ره وندن دسگا، خوساخوس و سرور پسند بیڌه منپیز ابۊ. Android گاشڌ ره وندن من پس زمینه ن مئدۊد کونه.</string>
|
||||
<string name="title_system_vpn_settings">سامووا VPN سیستوم</string>
|
||||
<string name="summary_system_vpn_settings">سامووا VPN من Android ن وا کۊنین سی ساموو کردن VPN همیشه ره وست، ٱر دسگا زس پشتیبانی اکونه.</string>
|
||||
<string name="toast_system_vpn_settings_unavailable">سامووا VPN سیستوم من دسرس نین.</string>
|
||||
|
||||
|
||||
<string name="subscription_updater_job_tips">وزیفه من پس زمینه کار اکونه وو ایلاعات پؽشرفت من اعلان نشووݩ داڌه ابۊ.</string>
|
||||
|
||||
@@ -172,8 +172,11 @@
|
||||
<string name="title_vpn_settings">تنظیمات VPN</string>
|
||||
<string name="title_pref_per_app_proxy">پروکسی به تفکیک برنامه</string>
|
||||
<string name="summary_pref_per_app_proxy">حالت عادی: برنامههای انتخابشده از پروکسی استفاده میکنند و برنامههای انتخابنشده مستقیماً متصل میشوند؛\nحالت دور زدن: برنامههای انتخابشده مستقیماً متصل میشوند و برنامههای انتخابنشده از پروکسی استفاده میکنند.\nگزینهٔ انتخاب خودکار برنامههایی که باید از پروکسی استفاده کنند در منو قرار دارد.</string>
|
||||
<string name="title_pref_is_booted">اتصال خودکار هنگام راه اندازی</string>
|
||||
<string name="summary_pref_is_booted">هنگام راه اندازی به طور خودکار به سرور انتخابی متصل می شود که ممکن است ناموفق باشد.</string>
|
||||
<string name="title_pref_is_booted">اتصال پس از راهاندازی دستگاه</string>
|
||||
<string name="summary_pref_is_booted">پس از راهاندازی دستگاه، بهطور خودکار به سرور انتخابشده متصل شود. ممکن است Android اجرای پسزمینه را محدود کند.</string>
|
||||
<string name="title_system_vpn_settings">تنظیمات VPN سیستم</string>
|
||||
<string name="summary_system_vpn_settings">تنظیمات VPN در Android را برای پیکربندی VPN همیشهروشن باز کنید، اگر دستگاه از آن پشتیبانی میکند.</string>
|
||||
<string name="toast_system_vpn_settings_unavailable">تنظیمات VPN سیستم در دسترس نیست.</string>
|
||||
|
||||
|
||||
<string name="subscription_updater_job_tips">این کار در پسزمینه اجرا میشود و پیشرفت آن در اعلان نمایش داده خواهد شد.</string>
|
||||
|
||||
@@ -172,8 +172,11 @@
|
||||
<string name="title_vpn_settings">Настройки VPN</string>
|
||||
<string name="title_pref_per_app_proxy">Прокси для выбранных приложений</string>
|
||||
<string name="summary_pref_per_app_proxy">Основной: выбранное приложение соединяется через прокси, не выбранное — напрямую;\nРежим обхода: выбранное приложение соединяется напрямую, не выбранное — через прокси.\nВ меню можно автоматически выбрать приложения, которые должны использовать прокси.</string>
|
||||
<string name="title_pref_is_booted">Автоподключение при запуске</string>
|
||||
<string name="summary_pref_is_booted">Автоматически подключаться к выбранному серверу при запуске приложения (может оказаться неудачным)</string>
|
||||
<string name="title_pref_is_booted">Подключаться после загрузки устройства</string>
|
||||
<string name="summary_pref_is_booted">Автоматически подключаться к выбранному серверу после загрузки устройства. Android может ограничивать запуск в фоне.</string>
|
||||
<string name="title_system_vpn_settings">Системные настройки VPN</string>
|
||||
<string name="summary_system_vpn_settings">Открыть настройки VPN в Android для включения постоянного VPN-подключения, если устройство его поддерживает.</string>
|
||||
<string name="toast_system_vpn_settings_unavailable">Системные настройки VPN недоступны.</string>
|
||||
|
||||
|
||||
<string name="subscription_updater_job_tips">Задача будет выполняться в фоновом режиме, информация о ходе выполнения будет отображаться в уведомлении.</string>
|
||||
|
||||
@@ -172,8 +172,11 @@
|
||||
<string name="title_vpn_settings">Cài đặt VPN</string>
|
||||
<string name="title_pref_per_app_proxy">Proxy theo từng ứng dụng</string>
|
||||
<string name="summary_pref_per_app_proxy">Thông thường: ứng dụng đã chọn dùng proxy, ứng dụng chưa chọn kết nối trực tiếp;\nChế độ bỏ qua: ứng dụng đã chọn kết nối trực tiếp, ứng dụng chưa chọn dùng proxy.\nTrong trình đơn có tùy chọn tự động chọn các ứng dụng sẽ dùng proxy.</string>
|
||||
<string name="title_pref_is_booted">Tự động kết nối khi khởi động</string>
|
||||
<string name="summary_pref_is_booted">Tự động kết nối với máy chủ đã chọn khi khởi động; thao tác này có thể không thành công</string>
|
||||
<string name="title_pref_is_booted">Kết nối sau khi thiết bị khởi động</string>
|
||||
<string name="summary_pref_is_booted">Tự động kết nối với máy chủ đã chọn sau khi thiết bị khởi động. Android có thể hạn chế khởi chạy trong nền.</string>
|
||||
<string name="title_system_vpn_settings">Cài đặt VPN hệ thống</string>
|
||||
<string name="summary_system_vpn_settings">Mở cài đặt VPN của Android để cấu hình VPN luôn bật, nếu thiết bị này hỗ trợ.</string>
|
||||
<string name="toast_system_vpn_settings_unavailable">Không có cài đặt VPN hệ thống.</string>
|
||||
|
||||
|
||||
<string name="subscription_updater_job_tips">Tác vụ sẽ chạy trong nền và thông tin tiến độ sẽ hiển thị trong thông báo.</string>
|
||||
|
||||
@@ -172,8 +172,11 @@
|
||||
<string name="title_vpn_settings">VPN 设置</string>
|
||||
<string name="title_pref_per_app_proxy">分应用代理</string>
|
||||
<string name="summary_pref_per_app_proxy">常规:勾选的应用使用代理,未勾选的应用直连;\n绕行模式:勾选的应用直连,未勾选的应用使用代理。\n菜单中可自动选择需要使用代理的应用。</string>
|
||||
<string name="title_pref_is_booted">开机时自动连接</string>
|
||||
<string name="summary_pref_is_booted">开机时自动连接选择的服务器,可能会不成功</string>
|
||||
<string name="title_pref_is_booted">设备启动后自动连接</string>
|
||||
<string name="summary_pref_is_booted">设备启动后自动连接到所选服务器。Android 可能会限制后台启动。</string>
|
||||
<string name="title_system_vpn_settings">系统 VPN 设置</string>
|
||||
<string name="summary_system_vpn_settings">打开 Android VPN 设置,配置始终开启的 VPN(如果此设备支持)。</string>
|
||||
<string name="toast_system_vpn_settings_unavailable">系统 VPN 设置不可用。</string>
|
||||
|
||||
|
||||
<string name="subscription_updater_job_tips">任务将在后台运行,在通知中会显示进度信息</string>
|
||||
|
||||
@@ -172,8 +172,11 @@
|
||||
<string name="title_vpn_settings">VPN 設定</string>
|
||||
<string name="title_pref_per_app_proxy">個別應用程式代理</string>
|
||||
<string name="summary_pref_per_app_proxy">一般:已勾選的應用程式使用代理,未勾選的應用程式直接連線;\n繞行模式:已勾選的應用程式直接連線,未勾選的應用程式使用代理。\n選單中可自動選取需要使用代理的應用程式。</string>
|
||||
<string name="title_pref_is_booted">開機時自動連線</string>
|
||||
<string name="summary_pref_is_booted">開機時自動連線選擇的伺服器,可能會不成功</string>
|
||||
<string name="title_pref_is_booted">裝置開機後自動連線</string>
|
||||
<string name="summary_pref_is_booted">裝置開機後自動連線至所選伺服器。Android 可能會限制背景啟動。</string>
|
||||
<string name="title_system_vpn_settings">系統 VPN 設定</string>
|
||||
<string name="summary_system_vpn_settings">開啟 Android VPN 設定,設定永遠開啟的 VPN(若此裝置支援)。</string>
|
||||
<string name="toast_system_vpn_settings_unavailable">系統 VPN 設定無法使用。</string>
|
||||
|
||||
|
||||
<string name="subscription_updater_job_tips">任務將在背景執行,通知中會顯示進度資訊。</string>
|
||||
|
||||
@@ -172,8 +172,11 @@
|
||||
<string name="title_vpn_settings">VPN Settings</string>
|
||||
<string name="title_pref_per_app_proxy">Per-app proxy</string>
|
||||
<string name="summary_pref_per_app_proxy">General: checked apps use the proxy, and unchecked apps connect directly;\nBypass mode: checked apps connect directly, and unchecked apps use the proxy.\nThe option to automatically select apps that should use the proxy is in the menu.</string>
|
||||
<string name="title_pref_is_booted">Auto connect at startup</string>
|
||||
<string name="summary_pref_is_booted">Automatically connects to the selected server at startup, which may be unsuccessful</string>
|
||||
<string name="title_pref_is_booted">Connect after device boot</string>
|
||||
<string name="summary_pref_is_booted">Automatically connect to the selected server after the device starts. Android may restrict background starts.</string>
|
||||
<string name="title_system_vpn_settings">System VPN settings</string>
|
||||
<string name="summary_system_vpn_settings">Open Android VPN settings to configure Always-on VPN, if supported by this device.</string>
|
||||
<string name="toast_system_vpn_settings_unavailable">System VPN settings are unavailable.</string>
|
||||
|
||||
|
||||
<string name="subscription_updater_job_tips">The task will run in the background, and progress information will be displayed in the notification.</string>
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.v2ray.ang.ui.settings
|
||||
|
||||
import android.app.Application
|
||||
import android.content.ComponentName
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.provider.Settings
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import org.mockito.Mockito.mockConstruction
|
||||
import org.mockito.kotlin.mock
|
||||
import org.mockito.kotlin.whenever
|
||||
|
||||
class SettingsViewModelTest {
|
||||
@Test
|
||||
fun vpnSettingsVisibilityFollowsActivityAvailability() = runBlocking(Dispatchers.IO) {
|
||||
val packageManager = mock<PackageManager>()
|
||||
val application = mock<Application>()
|
||||
whenever(application.packageManager).thenReturn(packageManager)
|
||||
var activity: ComponentName? = null
|
||||
|
||||
mockConstruction(Intent::class.java) { intent, context ->
|
||||
assertEquals(listOf(Settings.ACTION_VPN_SETTINGS), context.arguments())
|
||||
whenever(intent.resolveActivity(packageManager)).thenAnswer { activity }
|
||||
}.use {
|
||||
val viewModel = SettingsViewModel(application)
|
||||
assertFalse(viewModel.systemVpnSettingsAvailable.value)
|
||||
viewModel.refreshSystemVpnSettingsAvailability()
|
||||
assertFalse(viewModel.systemVpnSettingsAvailable.value)
|
||||
|
||||
activity = mock<ComponentName>()
|
||||
viewModel.refreshSystemVpnSettingsAvailability()
|
||||
assertTrue(viewModel.systemVpnSettingsAvailable.value)
|
||||
|
||||
activity = null
|
||||
viewModel.refreshSystemVpnSettingsAvailability()
|
||||
assertFalse(viewModel.systemVpnSettingsAvailable.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user