diff --git a/app/src/main/java/io/nekohasekai/sfa/bg/BoxService.kt b/app/src/main/java/io/nekohasekai/sfa/bg/BoxService.kt index 1c2f8f0..681e1f4 100644 --- a/app/src/main/java/io/nekohasekai/sfa/bg/BoxService.kt +++ b/app/src/main/java/io/nekohasekai/sfa/bg/BoxService.kt @@ -19,6 +19,7 @@ import io.nekohasekai.libbox.CommandServerHandler import io.nekohasekai.libbox.Libbox import io.nekohasekai.libbox.PProfServer import io.nekohasekai.libbox.PlatformInterface +import io.nekohasekai.libbox.SystemProxyStatus import io.nekohasekai.sfa.Application import io.nekohasekai.sfa.constant.Action import io.nekohasekai.sfa.constant.Alert @@ -164,6 +165,7 @@ class BoxService( } override fun serviceReload() { + status.postValue(Status.Starting) GlobalScope.launch(Dispatchers.IO) { val pfd = fileDescriptor if (pfd != null) { @@ -184,6 +186,19 @@ class BoxService( } } + override fun getSystemProxyStatus(): SystemProxyStatus { + val status = SystemProxyStatus() + if (service is VPNService) { + status.available = service.systemProxyAvailable + status.enabled = service.systemProxyEnabled + } + return status + } + + override fun setSystemProxyEnabled(isEnabled: Boolean) { + serviceReload() + } + @RequiresApi(Build.VERSION_CODES.M) private fun serviceUpdateIdleMode() { if (Application.powerManager.isDeviceIdleMode) { diff --git a/app/src/main/java/io/nekohasekai/sfa/bg/VPNService.kt b/app/src/main/java/io/nekohasekai/sfa/bg/VPNService.kt index 5be1522..eeed596 100644 --- a/app/src/main/java/io/nekohasekai/sfa/bg/VPNService.kt +++ b/app/src/main/java/io/nekohasekai/sfa/bg/VPNService.kt @@ -32,6 +32,9 @@ class VPNService : VpnService(), PlatformInterfaceWrapper { protect(fd) } + var systemProxyAvailable = false + var systemProxyEnabled = false + override fun openTun(options: TunOptions): Int { if (prepare(this) != null) error("android: missing vpn permission") @@ -124,8 +127,10 @@ class VPNService : VpnService(), PlatformInterfaceWrapper { } if (options.isHTTPProxyEnabled) { + systemProxyAvailable = true + systemProxyEnabled = Settings.systemProxyEnabled if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - builder.setHttpProxy( + if (systemProxyEnabled) builder.setHttpProxy( ProxyInfo.buildDirectProxy( options.httpProxyServer, options.httpProxyServerPort @@ -134,6 +139,9 @@ class VPNService : VpnService(), PlatformInterfaceWrapper { } else { error("android: tun.platform.http_proxy requires android 10 or higher") } + } else { + systemProxyAvailable = false + systemProxyEnabled = false } val pfd = diff --git a/app/src/main/java/io/nekohasekai/sfa/constant/SettingsKey.kt b/app/src/main/java/io/nekohasekai/sfa/constant/SettingsKey.kt index f843e66..40e32d9 100644 --- a/app/src/main/java/io/nekohasekai/sfa/constant/SettingsKey.kt +++ b/app/src/main/java/io/nekohasekai/sfa/constant/SettingsKey.kt @@ -13,6 +13,8 @@ object SettingsKey { const val PER_APP_PROXY_LIST = "per_app_proxy_list" const val PER_APP_PROXY_UPDATE_ON_CHANGE = "per_app_proxy_update_on_change" + const val SYSTEM_PROXY_ENABLED = "system_proxy_enabled" + // cache const val STARTED_BY_USER = "started_by_user" diff --git a/app/src/main/java/io/nekohasekai/sfa/database/ProfileManager.kt b/app/src/main/java/io/nekohasekai/sfa/database/ProfileManager.kt index c068906..ef33421 100644 --- a/app/src/main/java/io/nekohasekai/sfa/database/ProfileManager.kt +++ b/app/src/main/java/io/nekohasekai/sfa/database/ProfileManager.kt @@ -21,9 +21,15 @@ object ProfileManager { private val instance by lazy { Application.application.getDatabasePath(Path.PROFILES_DATABASE_PATH).parentFile?.mkdirs() - Room.databaseBuilder( - Application.application, ProfileDatabase::class.java, Path.PROFILES_DATABASE_PATH - ).fallbackToDestructiveMigration().setQueryExecutor { GlobalScope.launch { it.run() } } + Room + .databaseBuilder( + Application.application, + ProfileDatabase::class.java, + Path.PROFILES_DATABASE_PATH + ) + .fallbackToDestructiveMigration() + .enableMultiInstanceInvalidation() + .setQueryExecutor { GlobalScope.launch { it.run() } } .build() } diff --git a/app/src/main/java/io/nekohasekai/sfa/database/Settings.kt b/app/src/main/java/io/nekohasekai/sfa/database/Settings.kt index 3c9fd90..2f7b1a0 100644 --- a/app/src/main/java/io/nekohasekai/sfa/database/Settings.kt +++ b/app/src/main/java/io/nekohasekai/sfa/database/Settings.kt @@ -29,6 +29,7 @@ object Settings { Path.SETTINGS_DATABASE_PATH ).allowMainThreadQueries() .fallbackToDestructiveMigration() + .enableMultiInstanceInvalidation() .setQueryExecutor { GlobalScope.launch { it.run() } } .build() } @@ -55,6 +56,8 @@ object Settings { var perAppProxyList by dataStore.stringSet(SettingsKey.PER_APP_PROXY_LIST) { emptySet() } var perAppProxyUpdateOnChange by dataStore.int(SettingsKey.PER_APP_PROXY_UPDATE_ON_CHANGE) { PER_APP_PROXY_DISABLED } + var systemProxyEnabled by dataStore.boolean(SettingsKey.SYSTEM_PROXY_ENABLED) { true } + fun serviceClass(): Class<*> { return when (serviceMode) { ServiceMode.VPN -> VPNService::class.java diff --git a/app/src/main/java/io/nekohasekai/sfa/ui/dashboard/OverviewFragment.kt b/app/src/main/java/io/nekohasekai/sfa/ui/dashboard/OverviewFragment.kt index dd0bc5f..c3aa20c 100644 --- a/app/src/main/java/io/nekohasekai/sfa/ui/dashboard/OverviewFragment.kt +++ b/app/src/main/java/io/nekohasekai/sfa/ui/dashboard/OverviewFragment.kt @@ -65,12 +65,14 @@ class OverviewFragment : Fragment() { binding.profileList.addItemDecoration(divider) activity.serviceStatus.observe(viewLifecycleOwner) { binding.statusContainer.isVisible = it == Status.Starting || it == Status.Started - if (it != Status.Started) { + if (it == Status.Stopped) { binding.clashModeCard.isVisible = false + binding.systemProxyCard.isVisible = false } if (it == Status.Started) { statusClient.connect() clashModeClient.connect() + reloadSystemProxyStatus() } } ProfileManager.registerCallback(this::updateProfiles) @@ -89,6 +91,29 @@ class OverviewFragment : Fragment() { adapter?.reload() } + private fun reloadSystemProxyStatus() { + lifecycleScope.launch(Dispatchers.IO) { + val status = Libbox.newStandaloneCommandClient().systemProxyStatus + withContext(Dispatchers.Main) { + binding.systemProxyCard.isVisible = status.available + binding.systemProxyCard.isEnabled = true + binding.systemProxySwitch.setOnClickListener(null) + binding.systemProxySwitch.isChecked = status.enabled + binding.systemProxySwitch.setOnCheckedChangeListener { buttonView, isChecked -> + binding.systemProxyCard.isEnabled = false + lifecycleScope.launch(Dispatchers.IO) { + Settings.systemProxyEnabled = isChecked + runCatching { + Libbox.newStandaloneCommandClient().setSystemProxyEnabled(isChecked) + }.onFailure { + buttonView.context.errorDialogBuilder(it).show() + } + } + } + } + } + } + inner class StatusClient : CommandClient.Handler { override fun onConnected() { diff --git a/app/src/main/res/layout/fragment_dashboard_overview.xml b/app/src/main/res/layout/fragment_dashboard_overview.xml index 127091f..0ca87fe 100644 --- a/app/src/main/res/layout/fragment_dashboard_overview.xml +++ b/app/src/main/res/layout/fragment_dashboard_overview.xml @@ -429,18 +429,18 @@ + + + + + + + + + + Search URLTest Expand + HTTP Proxy \ No newline at end of file