owenclave / app/src/main/java/io/nekohasekai/sagernet/ui/SettingsPreferenceFragment.kt

github - dev - back to tree - upstream
/****************************************************************************** * * * Copyright (C) 2021 by nekohasekai * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ******************************************************************************/ package io.nekohasekai.sagernet.ui import android.Manifest import android.content.Intent import android.content.pm.PackageManager import android.icu.util.ULocale import android.net.Uri import android.os.Build import android.os.Bundle import android.provider.Settings import android.view.View import androidx.appcompat.app.AppCompatDelegate import androidx.core.app.ActivityCompat import androidx.core.os.LocaleListCompat import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding import androidx.preference.EditTextPreference import androidx.preference.ListPreference import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import androidx.preference.SwitchPreference import com.google.android.material.dialog.MaterialAlertDialogBuilder import io.nekohasekai.sagernet.* import io.nekohasekai.sagernet.Key.MODE_VPN import io.nekohasekai.sagernet.database.DataStore import io.nekohasekai.sagernet.database.preference.EditTextPreferenceModifiers import io.nekohasekai.sagernet.ktx.* import io.nekohasekai.sagernet.ui.profile.ProfileSettingsActivity import io.nekohasekai.sagernet.utils.PackageCache import io.nekohasekai.sagernet.utils.Theme import io.nekohasekai.sagernet.widget.ColorPickerPreference import io.nekohasekai.sagernet.widget.LinkOrContentPreference import kotlinx.coroutines.delay import java.io.File import java.util.Locale class SettingsPreferenceFragment : PreferenceFragmentCompat() { private lateinit var isProxyApps: SwitchPreference override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) listView.layoutManager = FixedLinearLayoutManager(listView) listView.setPadding(0,0,0,dp2px(64)) ViewCompat.setOnApplyWindowInsetsListener(listView) { v, insets -> val bars = insets.getInsets( WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout() ) v.updatePadding( left = bars.left, right = bars.right, bottom = bars.bottom + dp2px(64), ) insets } } val reloadListener = Preference.OnPreferenceChangeListener { _, _ -> needReload() true } override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { preferenceManager.preferenceDataStore = DataStore.configurationStore DataStore.initGlobal() addPreferencesFromResource(R.xml.global_preferences) // common isProxyApps = findPreference(Key.PROXY_APPS)!! val bypassLan = findPreference(Key.BYPASS_LAN)!! val requireHttp = findPreference(Key.REQUIRE_HTTP)!! val httpUsername = findPreference(Key.HTTP_USERNAME)!! val httpPassword = findPreference(Key.HTTP_PASSWORD)!! val appendHttpProxy = findPreference(Key.APPEND_HTTP_PROXY)!! val httpProxyException = findPreference(Key.HTTP_PROXY_EXCEPTION)!! // app settings findPreference(Key.APP_THEME)!!.setOnPreferenceChangeListener { _, newTheme -> val theme = Theme.getTheme(newTheme as Int) app.setTheme(theme) requireActivity().apply { ActivityCompat.recreate(this) } true } findPreference(Key.NIGHT_THEME)!!.setOnPreferenceChangeListener { _, newValue -> Theme.currentNightMode = (newValue as String).toInt() Theme.applyNightTheme() requireActivity().apply { ActivityCompat.recreate(this) } true } fun getLanguageDisplayName(code: String): String = run { return when (code) { "" -> getString(R.string.language_system_default) "ar" -> getString(R.string.language_ar_display_name) "en-US" -> getString(R.string.language_en_display_name) "es" -> getString(R.string.language_es_display_name) "fa" -> getString(R.string.language_fa_display_name) "fr" -> getString(R.string.language_fr_display_name) "id" -> getString(R.string.language_id_display_name) "it" -> getString(R.string.language_it_display_name) "ja" -> getString(R.string.language_ja_display_name) "ko" -> getString(R.string.language_ko_display_name) "nb-NO" -> getString(R.string.language_nb_NO_display_name) "ru" -> getString(R.string.language_ru_display_name) "ta" -> getString(R.string.language_ta_display_name) "tr" -> getString(R.string.language_tr_display_name) "zh-Hans-CN" -> getString(R.string.language_zh_Hans_CN_display_name) "zh-Hant-TW" -> getString(R.string.language_zh_Hant_TW_display_name) else -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { ULocale.forLanguageTag(code).displayName } else { Locale.forLanguageTag(code).displayName } } } val appLanguage = findPreference(Key.APP_LANGUAGE)!! val locale = when (val value = AppCompatDelegate.getApplicationLocales().toLanguageTags()) { // https://stackoverflow.com/questions/13291578/how-to-localize-an-android-app-in-indonesian-language // Some old Android versions still return "in". "in" -> "id" else -> value } appLanguage.summary = getLanguageDisplayName(locale) appLanguage.value = if (locale in resources.getStringArray(R.array.language_value)) locale else "" appLanguage.setOnPreferenceChangeListener { _, newValue -> newValue as String AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags(newValue)) // "id" always works appLanguage.summary = getLanguageDisplayName(newValue) appLanguage.value = newValue true } val serviceMode = findPreference(Key.SERVICE_MODE)!! val tunImplementation = findPreference(Key.TUN_IMPLEMENTATION)!! val mtu = findPreference(Key.MTU)!! val enableVPNInterfaceIPv6Address = findPreference(Key.ENABLE_VPN_INTERFACE_IPV6_ADDRESS)!! val allowAppsBypassVpn = findPreference(Key.ALLOW_APPS_BYPASS_VPN)!! val meteredNetwork = findPreference(Key.METERED_NETWORK)!! val enablePcap = findPreference(Key.ENABLE_PCAP)!! val discardICMP = findPreference(Key.DISCARD_ICMP)!! val appTrafficStatistics = findPreference(Key.APP_TRAFFIC_STATISTICS)!! serviceMode.setOnPreferenceChangeListener { _, newValue -> newValue as String tunImplementation.isEnabled = newValue == MODE_VPN mtu.isEnabled = newValue == MODE_VPN enableVPNInterfaceIPv6Address.isEnabled = newValue == MODE_VPN allowAppsBypassVpn.isEnabled = newValue == MODE_VPN discardICMP.isEnabled = newValue == MODE_VPN meteredNetwork.isEnabled = newValue == MODE_VPN enablePcap.isEnabled = newValue == MODE_VPN && tunImplementation.value.toInt() == TunImplementation.GVISOR appTrafficStatistics.isEnabled = newValue == MODE_VPN isProxyApps.isEnabled = newValue == MODE_VPN bypassLan.isEnabled = newValue == MODE_VPN if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { appendHttpProxy.isVisible = requireHttp.isChecked && newValue == MODE_VPN && httpUsername.isVisible && httpUsername.text.isNullOrEmpty() && httpPassword.isVisible && httpPassword.text.isNullOrEmpty() httpProxyException.isVisible = requireHttp.isChecked && newValue == MODE_VPN && appendHttpProxy.isVisible && appendHttpProxy.isChecked && httpUsername.isVisible && httpUsername.text.isNullOrEmpty() && httpPassword.isVisible && httpPassword.text.isNullOrEmpty() } if (SagerNet.started) { runOnDefaultDispatcher { SagerNet.stopService() delay(300) // FIXME: Why this is needed? SagerNet.startService() } } true } tunImplementation.isEnabled = serviceMode.value == MODE_VPN tunImplementation.setOnPreferenceChangeListener { _, newValue -> if ((newValue as String).toInt() == TunImplementation.SYSTEM && Build.VERSION.SDK_INT >= Build.VERSION_CODES.CINNAMON_BUN && app.checkSelfPermission(Manifest.permission.ACCESS_LOCAL_NETWORK) != PackageManager.PERMISSION_GRANTED) { MaterialAlertDialogBuilder(requireContext()).apply { setTitle(R.string.error_title) setMessage(R.string.nearby_devices_permission_notice) setNeutralButton(R.string.open_settings) { _, _ -> try { startActivity(Intent().apply { action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS data = Uri.fromParts("package", app.packageName, null) }) } catch (e: Exception) { snackbar(e.readableMessage).show() } } setPositiveButton(android.R.string.ok, null) }.show() } enablePcap.isEnabled = serviceMode.value == MODE_VPN && newValue.toInt() == TunImplementation.GVISOR if (SagerNet.started) { runOnDefaultDispatcher { SagerNet.reloadService() } } true } mtu.isEnabled = serviceMode.value == MODE_VPN mtu.setOnBindEditTextListener(EditTextPreferenceModifiers.Number) mtu.onPreferenceChangeListener = reloadListener enableVPNInterfaceIPv6Address.isEnabled = serviceMode.value == MODE_VPN enableVPNInterfaceIPv6Address.onPreferenceChangeListener = reloadListener allowAppsBypassVpn.isEnabled = serviceMode.value == MODE_VPN allowAppsBypassVpn.onPreferenceChangeListener = reloadListener if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { meteredNetwork.remove() } meteredNetwork.isEnabled = serviceMode.value == MODE_VPN meteredNetwork.onPreferenceChangeListener = reloadListener enablePcap.isEnabled = serviceMode.value == MODE_VPN && tunImplementation.value.toInt() == TunImplementation.GVISOR enablePcap.setOnPreferenceChangeListener { _, newValue -> if (newValue as Boolean) { val path = File( app.getExternalFilesDir(null)?.apply { mkdirs() } ?: app.filesDir, "pcap" ).absolutePath MaterialAlertDialogBuilder(requireContext()).apply { setTitle(R.string.pcap) setMessage(resources.getString(R.string.pcap_notice, path)) setPositiveButton(android.R.string.ok) { _, _ -> needReload() } setNegativeButton(android.R.string.copy) { _, _ -> SagerNet.trySetPrimaryClip(path) snackbar(R.string.copy_success).show() } }.show() } needReload() true } discardICMP.isEnabled = serviceMode.value == MODE_VPN discardICMP.onPreferenceChangeListener = reloadListener appTrafficStatistics.isEnabled = serviceMode.value == MODE_VPN appTrafficStatistics.setOnPreferenceChangeListener { _, newValue -> newValue as Boolean if (newValue) { PackageCache.awaitLoadSync() } needReload() true } val profileTrafficStatistics = findPreference(Key.PROFILE_TRAFFIC_STATISTICS)!! val speedInterval = findPreference(Key.SPEED_INTERVAL)!! val showDirectSpeed = findPreference(Key.SHOW_DIRECT_SPEED)!! profileTrafficStatistics.setOnPreferenceChangeListener { _, newValue -> newValue as Boolean speedInterval.isEnabled = newValue showDirectSpeed.isEnabled = newValue needReload() true } speedInterval.isEnabled = profileTrafficStatistics.isChecked speedInterval.onPreferenceChangeListener = reloadListener showDirectSpeed.isEnabled = profileTrafficStatistics.isChecked showDirectSpeed.onPreferenceChangeListener = reloadListener findPreference(Key.LOG_LEVEL)!!.setOnPreferenceChangeListener { _, newValue -> if ((newValue as String).toInt() == LogLevel.DEBUG && !DataStore.logLevelDebugWarningDisable) { MaterialAlertDialogBuilder(requireContext()).apply { setMessage(R.string.debug_log_sum) setPositiveButton(android.R.string.ok, null) setNeutralButton(R.string.do_not_show_again, { _, _ -> DataStore.logLevelDebugWarningDisable = true }) }.show() } needReload() true } findPreference(Key.PROVIDER_ROOT_CA)!!.setOnPreferenceChangeListener { _, newValue -> if ((newValue as String).toInt() == RootCAProvider.CUSTOM) { runOnMainDispatcher { val context = requireContext() MaterialAlertDialogBuilder(context) .setMessage(getString(R.string.custom_root_ca_hint, context.packageName)) .setPositiveButton(android.R.string.ok, null) .show() } } true } // route settings findPreference(Key.ROUTE_MODE)!!.onPreferenceChangeListener = reloadListener isProxyApps.isEnabled = serviceMode.value == MODE_VPN isProxyApps.setOnPreferenceChangeListener { _, newValue -> startActivity(Intent(activity, AppManagerActivity::class.java)) newValue as Boolean } bypassLan.isEnabled = serviceMode.value == MODE_VPN bypassLan.setOnPreferenceChangeListener { _, _ -> needReload() true } findPreference(Key.DOMAIN_STRATEGY)!!.onPreferenceChangeListener = reloadListener val trafficSniffing = findPreference(Key.TRAFFIC_SNIFFING)!! val destinationOverride = findPreference(Key.DESTINATION_OVERRIDE)!! val hijackDns = findPreference(Key.HIJACK_DNS)!! trafficSniffing.setOnPreferenceChangeListener { _, newValue -> destinationOverride.isEnabled = newValue as Boolean hijackDns.isEnabled = newValue needReload() true } destinationOverride.isEnabled = trafficSniffing.isChecked destinationOverride.onPreferenceChangeListener = reloadListener hijackDns.isEnabled = trafficSniffing.isChecked hijackDns.onPreferenceChangeListener = reloadListener findPreference(Key.OUTBOUND_DOMAIN_STRATEGY)!!.onPreferenceChangeListener = reloadListener findPreference(Key.OUTBOUND_DOMAIN_STRATEGY_FOR_DIRECT)!!.onPreferenceChangeListener = reloadListener findPreference(Key.OUTBOUND_DOMAIN_STRATEGY_FOR_SERVER)!!.onPreferenceChangeListener = reloadListener val rulesProvider = findPreference(Key.RULES_PROVIDER)!! val rulesGeositeUrl = findPreference(Key.RULES_GEOSITE_URL)!! val rulesGeoipUrl = findPreference(Key.RULES_GEOIP_URL)!! rulesProvider.setOnPreferenceChangeListener { _, newValue -> val provider = (newValue as String).toInt() rulesGeositeUrl.isVisible = provider == 3 rulesGeoipUrl.isVisible = provider == 3 true } rulesGeositeUrl.isVisible = DataStore.rulesProvider == 3 rulesGeoipUrl.isVisible = DataStore.rulesProvider == 3 // protocol settings val enableFragment = findPreference(Key.ENABLE_FRAGMENT)!! val enableFragmentForDirect = findPreference(Key.ENABLE_FRAGMENT_FOR_DIRECT)!! val fragmentMethod = findPreference(Key.FRAGMENT_METHOD)!! enableFragment.setOnPreferenceChangeListener { _, newValue -> newValue as Boolean enableFragmentForDirect.isVisible = newValue fragmentMethod.isVisible = newValue true } enableFragmentForDirect.isVisible = enableFragment.isChecked fragmentMethod.isVisible = enableFragment.isChecked // DNS settings findPreference(Key.REMOTE_DNS)!!.onPreferenceChangeListener = reloadListener findPreference(Key.REMOTE_DNS_QUERY_STRATEGY)!!.onPreferenceChangeListener = reloadListener findPreference(Key.EDNS_CLIENT_IP)!!.onPreferenceChangeListener = reloadListener val useLocalDnsAsDirectDns = findPreference(Key.USE_LOCAL_DNS_AS_DIRECT_DNS)!! val directDns = findPreference(Key.DIRECT_DNS)!! directDns.onPreferenceChangeListener = reloadListener useLocalDnsAsDirectDns.setOnPreferenceChangeListener { _, newValue -> directDns.isEnabled = newValue == false needReload() true } directDns.isEnabled = !useLocalDnsAsDirectDns.isChecked findPreference(Key.DIRECT_DNS_QUERY_STRATEGY)!!.onPreferenceChangeListener = reloadListener val useLocalDnsAsBootstrapDns = findPreference(Key.USE_LOCAL_DNS_AS_BOOTSTRAP_DNS)!! val bootstrapDns = findPreference(Key.BOOTSTRAP_DNS)!! bootstrapDns.onPreferenceChangeListener = reloadListener useLocalDnsAsBootstrapDns.setOnPreferenceChangeListener { _, newValue -> bootstrapDns.isEnabled = newValue == false needReload() true } bootstrapDns.isEnabled = !useLocalDnsAsBootstrapDns.isChecked val dnsHosts = findPreference(Key.DNS_HOSTS)!! dnsHosts.setOnBindEditTextListener(EditTextPreferenceModifiers.Hosts) dnsHosts.dialogMessage = getString(R.string.one_per_line_format, "example.com 127.0.0.1\nwww.example.com 127.0.0.1 127.0.0.2") dnsHosts.onPreferenceChangeListener = reloadListener findPreference(Key.ENABLE_DNS_ROUTING)!!.onPreferenceChangeListener = reloadListener findPreference(Key.ENABLE_FAKEDNS)!!.onPreferenceChangeListener = reloadListener // inbound settings findPreference(Key.ALLOW_ACCESS)!!.onPreferenceChangeListener = reloadListener val allowAccess = findPreference(Key.ALLOW_ACCESS)!! allowAccess.setOnPreferenceChangeListener { _, newValue -> newValue as Boolean if (newValue && Build.VERSION.SDK_INT >= Build.VERSION_CODES.CINNAMON_BUN && app.checkSelfPermission(Manifest.permission.ACCESS_LOCAL_NETWORK) != PackageManager.PERMISSION_GRANTED) { MaterialAlertDialogBuilder(requireContext()).apply { setTitle(R.string.error_title) setMessage(R.string.nearby_devices_permission_notice) setNeutralButton(R.string.open_settings) { _, _ -> try { startActivity(Intent().apply { action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS data = Uri.fromParts("package", app.packageName, null) }) } catch (e: Exception) { snackbar(e.readableMessage).show() } } setPositiveButton(android.R.string.ok, null) }.show() } needReload() true } val requireSocks = findPreference(Key.REQUIRE_SOCKS)!! val requireTransproxy = findPreference(Key.REQUIRE_TRANSPROXY)!! val requireDns = findPreference(Key.REQUIRE_DNS_INBOUND)!! allowAccess.isVisible = requireSocks.isChecked || requireHttp.isChecked || requireTransproxy.isChecked || requireDns.isChecked val portSocks5 = findPreference(Key.SOCKS_PORT)!! portSocks5.setOnBindEditTextListener(EditTextPreferenceModifiers.Port) portSocks5.isVisible = requireSocks.isChecked portSocks5.onPreferenceChangeListener = reloadListener val socks5UDP = findPreference(Key.SOCKS_UDP)!! val socks5Username = findPreference(Key.SOCKS_USERNAME)!! socks5Username.isVisible = requireSocks.isChecked socks5Username.setOnPreferenceChangeListener { _, newValue -> newValue as String if (newValue.isNotEmpty() && socks5UDP.isVisible && socks5UDP.isChecked && !DataStore.socksUDPWarningDisable) runOnMainDispatcher { MaterialAlertDialogBuilder(requireContext()).apply { setMessage(R.string.socks5_udp_authentication_warning) setPositiveButton(android.R.string.ok, null) setNeutralButton(R.string.do_not_show_again, { _, _ -> DataStore.socksUDPWarningDisable = true }) }.show() } needReload() true } val socks5Password = findPreference(Key.SOCKS_PASSWORD)!! socks5Password.summaryProvider = ProfileSettingsActivity.PasswordSummaryProvider socks5Password.isVisible = requireSocks.isChecked socks5Password.setOnPreferenceChangeListener { _, newValue -> newValue as String if (newValue.isNotEmpty() && socks5UDP.isVisible && socks5UDP.isChecked && !DataStore.socksUDPWarningDisable) runOnMainDispatcher { MaterialAlertDialogBuilder(requireContext()).apply { setMessage(R.string.socks5_udp_authentication_warning) setPositiveButton(android.R.string.ok, null) setNeutralButton(R.string.do_not_show_again, { _, _ -> DataStore.socksUDPWarningDisable = true }) }.show() } needReload() true } socks5UDP.isVisible = requireSocks.isChecked socks5UDP.setOnPreferenceChangeListener { _, newValue -> newValue as Boolean if (newValue && ((socks5Username.isVisible && !socks5Username.text.isNullOrEmpty()) || (socks5Password.isVisible && !socks5Password.text.isNullOrEmpty())) && !DataStore.socksUDPWarningDisable) runOnMainDispatcher { MaterialAlertDialogBuilder(requireContext()).apply { setMessage(R.string.socks5_udp_authentication_warning) setPositiveButton(android.R.string.ok, null) setNeutralButton(R.string.do_not_show_again, { _, _ -> DataStore.socksUDPWarningDisable = true }) }.show() } needReload() true } requireSocks.setOnPreferenceChangeListener { _, newValue -> portSocks5.isVisible = newValue as Boolean socks5Username.isVisible = newValue socks5Password.isVisible = newValue socks5UDP.isVisible = newValue allowAccess.isVisible = newValue || requireHttp.isChecked || requireTransproxy.isChecked || requireDns.isChecked needReload() true } val portHttp = findPreference(Key.HTTP_PORT)!! portHttp.setOnBindEditTextListener(EditTextPreferenceModifiers.Port) portHttp.isVisible = requireHttp.isChecked portHttp.onPreferenceChangeListener = reloadListener httpUsername.isVisible = requireHttp.isChecked if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { httpUsername.onPreferenceChangeListener = reloadListener } else { httpUsername.onPreferenceChangeListener = { _, newValue -> newValue as String appendHttpProxy.isVisible = serviceMode.value == MODE_VPN && newValue.isEmpty() && httpPassword.isVisible && httpPassword.text.isNullOrEmpty() httpProxyException.isVisible = serviceMode.value == MODE_VPN && newValue.isEmpty() && appendHttpProxy.isVisible && appendHttpProxy.isChecked && newValue.isEmpty() && httpPassword.isVisible && httpPassword.text.isNullOrEmpty() needReload() true } } httpPassword.summaryProvider = ProfileSettingsActivity.PasswordSummaryProvider httpPassword.isVisible = requireHttp.isChecked if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { httpPassword.onPreferenceChangeListener = reloadListener } else { httpPassword.onPreferenceChangeListener = { _, newValue -> newValue as String appendHttpProxy.isVisible = serviceMode.value == MODE_VPN && newValue.isEmpty() && httpUsername.isVisible && httpUsername.text.isNullOrEmpty() httpProxyException.isVisible = serviceMode.value == MODE_VPN && newValue.isEmpty() && appendHttpProxy.isVisible && appendHttpProxy.isChecked && newValue.isEmpty() && httpUsername.isVisible && httpUsername.text.isNullOrEmpty() needReload() true } } if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { requireHttp.setOnPreferenceChangeListener { _, newValue -> portHttp.isVisible = newValue as Boolean httpUsername.isVisible = newValue httpPassword.isVisible = newValue allowAccess.isVisible = requireSocks.isChecked || newValue || requireTransproxy.isChecked || requireDns.isChecked needReload() true } appendHttpProxy.remove() httpProxyException.remove() } else { requireHttp.setOnPreferenceChangeListener { _, newValue -> portHttp.isVisible = newValue as Boolean httpUsername.isVisible = newValue httpPassword.isVisible = newValue appendHttpProxy.isVisible = newValue && serviceMode.value == MODE_VPN && httpUsername.isVisible && httpUsername.text.isNullOrEmpty() && httpPassword.isVisible && httpPassword.text.isNullOrEmpty() httpProxyException.isVisible = newValue && serviceMode.value == MODE_VPN && appendHttpProxy.isVisible && appendHttpProxy.isChecked && httpUsername.isVisible && httpUsername.text.isNullOrEmpty() && httpPassword.isVisible && httpPassword.text.isNullOrEmpty() allowAccess.isVisible = requireSocks.isChecked || newValue || requireTransproxy.isChecked || requireDns.isChecked needReload() true } appendHttpProxy.isVisible = requireHttp.isChecked && serviceMode.value == MODE_VPN && httpUsername.isVisible && httpUsername.text.isNullOrEmpty() && httpPassword.isVisible && httpPassword.text.isNullOrEmpty() appendHttpProxy.setOnPreferenceChangeListener { _, newValue -> httpProxyException.isVisible = newValue as Boolean needReload() true } httpProxyException.isVisible = requireHttp.isChecked && serviceMode.value == MODE_VPN && appendHttpProxy.isVisible && appendHttpProxy.isChecked && httpUsername.isVisible && httpUsername.text.isNullOrEmpty() && httpPassword.isVisible && httpPassword.text.isNullOrEmpty() httpProxyException.onPreferenceChangeListener = reloadListener } val transproxyPort = findPreference(Key.TRANSPROXY_PORT)!! requireTransproxy.setOnPreferenceChangeListener { _, newValue -> transproxyPort.isVisible = newValue as Boolean allowAccess.isVisible = requireSocks.isChecked || requireHttp.isChecked || newValue || requireDns.isChecked needReload() true } transproxyPort.setOnBindEditTextListener(EditTextPreferenceModifiers.Port) transproxyPort.isVisible = requireTransproxy.isChecked transproxyPort.onPreferenceChangeListener = reloadListener val portLocalDns = findPreference(Key.LOCAL_DNS_PORT)!! requireDns.setOnPreferenceChangeListener { _, newValue -> portLocalDns.isVisible = newValue as Boolean allowAccess.isVisible = requireSocks.isChecked || requireHttp.isChecked || requireTransproxy.isChecked || newValue needReload() true } portLocalDns.setOnBindEditTextListener(EditTextPreferenceModifiers.Port) portLocalDns.isVisible = requireDns.isChecked portLocalDns.onPreferenceChangeListener = reloadListener findPreference(Key.PPROF_SERVER)!!.apply { isVisible = DataStore.enableDebug onPreferenceChangeListener = reloadListener } findPreference(Key.EXPERIMENTAL_FLAGS)!!.isVisible = DataStore.enableDebug // misc settings findPreference(Key.SHOW_GROUP_NAME)!!.onPreferenceChangeListener = reloadListener findPreference(Key.ACQUIRE_WAKE_LOCK)!!.onPreferenceChangeListener = reloadListener findPreference(Key.FAB_STYLE)!!.setOnPreferenceChangeListener { _, _ -> requireActivity().apply { this.finish() startActivity(intent) } true } } override fun onResume() { super.onResume() if (::isProxyApps.isInitialized) { isProxyApps.isChecked = DataStore.proxyApps } } }