From eb7efaaac97089da775754785cd271e253bd54bd Mon Sep 17 00:00:00 2001 From: Koitharu Date: Sat, 20 May 2023 15:32:09 +0300 Subject: [PATCH] Add proxy support #376 --- .../org/koitharu/kotatsu/core/logs/Loggers.kt | 4 +- .../kotatsu/core/network/AppProxySelector.kt | 43 +++++++++++++ .../kotatsu/core/network/HttpClients.kt | 4 +- .../kotatsu/core/network/NetworkModule.kt | 1 + .../kotatsu/core/prefs/AppSettings.kt | 17 ++++++ .../koitharu/kotatsu/local/data/Qualifiers.kt | 2 +- .../settings/ContentSettingsFragment.kt | 21 +++++++ .../kotatsu/settings/ProxySettingsFragment.kt | 60 +++++++++++++++++++ .../settings/utils/EditTextBindListener.kt | 2 +- app/src/main/res/values/arrays.xml | 7 ++- app/src/main/res/values/constants.xml | 5 ++ app/src/main/res/values/strings.xml | 4 ++ app/src/main/res/xml/pref_content.xml | 5 ++ app/src/main/res/xml/pref_proxy.xml | 24 ++++++++ 14 files changed, 192 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/org/koitharu/kotatsu/core/network/AppProxySelector.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/settings/ProxySettingsFragment.kt create mode 100644 app/src/main/res/xml/pref_proxy.xml diff --git a/app/src/main/java/org/koitharu/kotatsu/core/logs/Loggers.kt b/app/src/main/java/org/koitharu/kotatsu/core/logs/Loggers.kt index 47eeea605..008ca7d92 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/logs/Loggers.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/logs/Loggers.kt @@ -3,9 +3,9 @@ package org.koitharu.kotatsu.core.logs import javax.inject.Qualifier @Qualifier -@Retention(AnnotationRetention.SOURCE) +@Retention(AnnotationRetention.BINARY) annotation class TrackerLogger @Qualifier -@Retention(AnnotationRetention.SOURCE) +@Retention(AnnotationRetention.BINARY) annotation class SyncLogger diff --git a/app/src/main/java/org/koitharu/kotatsu/core/network/AppProxySelector.kt b/app/src/main/java/org/koitharu/kotatsu/core/network/AppProxySelector.kt new file mode 100644 index 000000000..5beb6e42a --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/core/network/AppProxySelector.kt @@ -0,0 +1,43 @@ +package org.koitharu.kotatsu.core.network + +import org.koitharu.kotatsu.core.prefs.AppSettings +import org.koitharu.kotatsu.util.ext.printStackTraceDebug +import java.io.IOException +import java.net.InetSocketAddress +import java.net.Proxy +import java.net.ProxySelector +import java.net.SocketAddress +import java.net.URI + +class AppProxySelector( + private val settings: AppSettings, +) : ProxySelector() { + + private var cachedProxy: Proxy? = null + + override fun select(uri: URI?): List { + return listOf(getProxy()) + } + + override fun connectFailed(uri: URI?, sa: SocketAddress?, ioe: IOException?) { + ioe?.printStackTraceDebug() + } + + private fun getProxy(): Proxy { + val type = settings.proxyType + val address = settings.proxyAddress + val port = settings.proxyPort + if (type == Proxy.Type.DIRECT || address.isNullOrEmpty() || port == 0) { + return Proxy.NO_PROXY + } + cachedProxy?.let { + val addr = it.address() as? InetSocketAddress + if (addr != null && it.type() == type && addr.port == port && addr.hostString == address) { + return it + } + } + val proxy = Proxy(type, InetSocketAddress(address, port)) + cachedProxy = proxy + return proxy + } +} diff --git a/app/src/main/java/org/koitharu/kotatsu/core/network/HttpClients.kt b/app/src/main/java/org/koitharu/kotatsu/core/network/HttpClients.kt index 45e1b5d45..fb69fe291 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/network/HttpClients.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/network/HttpClients.kt @@ -3,9 +3,9 @@ package org.koitharu.kotatsu.core.network import javax.inject.Qualifier @Qualifier -@Retention(AnnotationRetention.SOURCE) +@Retention(AnnotationRetention.BINARY) annotation class BaseHttpClient @Qualifier -@Retention(AnnotationRetention.SOURCE) +@Retention(AnnotationRetention.BINARY) annotation class MangaHttpClient diff --git a/app/src/main/java/org/koitharu/kotatsu/core/network/NetworkModule.kt b/app/src/main/java/org/koitharu/kotatsu/core/network/NetworkModule.kt index e528a21a8..a60c5db64 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/network/NetworkModule.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/network/NetworkModule.kt @@ -58,6 +58,7 @@ interface NetworkModule { readTimeout(60, TimeUnit.SECONDS) writeTimeout(20, TimeUnit.SECONDS) cookieJar(cookieJar) + proxySelector(AppProxySelector(settings)) dns(DoHManager(cache, settings)) if (settings.isSSLBypassEnabled) { bypassSSLErrors() diff --git a/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSettings.kt b/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSettings.kt index 03d0c04e8..fe2e24192 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSettings.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSettings.kt @@ -25,6 +25,7 @@ import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.parsers.util.mapToSet import org.koitharu.kotatsu.shelf.domain.ShelfSection import java.io.File +import java.net.Proxy import java.util.Collections import java.util.EnumSet import java.util.Locale @@ -276,6 +277,18 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) { val isSSLBypassEnabled: Boolean get() = prefs.getBoolean(KEY_SSL_BYPASS, false) + val proxyType: Proxy.Type + get() { + val raw = prefs.getString(KEY_PROXY_TYPE, null) ?: return Proxy.Type.DIRECT + return enumValues().find { it.name == raw } ?: Proxy.Type.DIRECT + } + + val proxyAddress: String? + get() = prefs.getString(KEY_PROXY_ADDRESS, null) + + val proxyPort: Int + get() = prefs.getString(KEY_PROXY_PORT, null)?.toIntOrNull() ?: 0 + var localListOrder: SortOrder get() = prefs.getEnumValue(KEY_LOCAL_LIST_ORDER, SortOrder.NEWEST) set(value) = prefs.edit { putEnumValue(KEY_LOCAL_LIST_ORDER, value) } @@ -413,6 +426,10 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) { const val KEY_SSL_BYPASS = "ssl_bypass" const val KEY_READER_AUTOSCROLL_SPEED = "as_speed" const val KEY_MIRROR_SWITCHING = "mirror_switching" + const val KEY_PROXY = "proxy" + const val KEY_PROXY_TYPE = "proxy_type" + const val KEY_PROXY_ADDRESS = "proxy_address" + const val KEY_PROXY_PORT = "proxy_port" // About const val KEY_APP_UPDATE = "app_update" diff --git a/app/src/main/java/org/koitharu/kotatsu/local/data/Qualifiers.kt b/app/src/main/java/org/koitharu/kotatsu/local/data/Qualifiers.kt index ed5bb669e..abc8d7714 100644 --- a/app/src/main/java/org/koitharu/kotatsu/local/data/Qualifiers.kt +++ b/app/src/main/java/org/koitharu/kotatsu/local/data/Qualifiers.kt @@ -3,5 +3,5 @@ package org.koitharu.kotatsu.local.data import javax.inject.Qualifier @Qualifier -@Retention(AnnotationRetention.SOURCE) +@Retention(AnnotationRetention.BINARY) annotation class LocalStorageChanges diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/ContentSettingsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/settings/ContentSettingsFragment.kt index bad3d8632..e062e6800 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/ContentSettingsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/ContentSettingsFragment.kt @@ -24,6 +24,7 @@ import org.koitharu.kotatsu.local.data.LocalStorageManager import org.koitharu.kotatsu.parsers.util.names import org.koitharu.kotatsu.util.ext.printStackTraceDebug import java.io.File +import java.net.Proxy import javax.inject.Inject @AndroidEntryPoint @@ -62,6 +63,7 @@ class ContentSettingsFragment : if (settings.isSuggestionsEnabled) R.string.enabled else R.string.disabled, ) bindRemoteSourcesSummary() + bindProxySummary() settings.subscribe(this) } @@ -93,6 +95,12 @@ class ContentSettingsFragment : AppSettings.KEY_SSL_BYPASS -> { Snackbar.make(listView, R.string.settings_apply_restart_required, Snackbar.LENGTH_INDEFINITE).show() } + + AppSettings.KEY_PROXY_TYPE, + AppSettings.KEY_PROXY_ADDRESS, + AppSettings.KEY_PROXY_PORT -> { + bindProxySummary() + } } } @@ -130,6 +138,19 @@ class ContentSettingsFragment : } } + private fun bindProxySummary() { + findPreference(AppSettings.KEY_PROXY)?.run { + val type = settings.proxyType + val address = settings.proxyAddress + val port = settings.proxyPort + summary = if (type == Proxy.Type.DIRECT || address.isNullOrEmpty() || port == 0) { + context.getString(R.string.disabled) + } else { + "$type $address:$port" + } + } + } + private fun updateDownloadsConstraints() { val preference = findPreference(AppSettings.KEY_DOWNLOADS_WIFI) viewLifecycleScope.launch { diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/ProxySettingsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/settings/ProxySettingsFragment.kt new file mode 100644 index 000000000..7b86847c4 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/settings/ProxySettingsFragment.kt @@ -0,0 +1,60 @@ +package org.koitharu.kotatsu.settings + +import android.content.SharedPreferences +import android.os.Bundle +import android.view.View +import android.view.inputmethod.EditorInfo +import androidx.preference.EditTextPreference +import androidx.preference.Preference +import dagger.hilt.android.AndroidEntryPoint +import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.prefs.AppSettings +import org.koitharu.kotatsu.core.ui.BasePreferenceFragment +import org.koitharu.kotatsu.settings.utils.EditTextBindListener +import java.net.Proxy + +@AndroidEntryPoint +class ProxySettingsFragment : BasePreferenceFragment(R.string.proxy), + SharedPreferences.OnSharedPreferenceChangeListener { + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + addPreferencesFromResource(R.xml.pref_proxy) + findPreference(AppSettings.KEY_PROXY_ADDRESS)?.setOnBindEditTextListener( + EditTextBindListener( + inputType = EditorInfo.TYPE_CLASS_TEXT or EditorInfo.TYPE_TEXT_VARIATION_URI, + hint = null, + validator = DomainValidator(), + ), + ) + findPreference(AppSettings.KEY_PROXY_PORT)?.setOnBindEditTextListener( + EditTextBindListener( + inputType = EditorInfo.TYPE_CLASS_NUMBER, + hint = null, + validator = null, + ), + ) + updateDependencies() + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + settings.subscribe(this) + } + + override fun onDestroyView() { + settings.unsubscribe(this) + super.onDestroyView() + } + + override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { + when (key) { + AppSettings.KEY_PROXY_TYPE -> updateDependencies() + } + } + + private fun updateDependencies() { + val isProxyEnabled = settings.proxyType != Proxy.Type.DIRECT + findPreference(AppSettings.KEY_PROXY_ADDRESS)?.isEnabled = isProxyEnabled + findPreference(AppSettings.KEY_PROXY_PORT)?.isEnabled = isProxyEnabled + } +} diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/utils/EditTextBindListener.kt b/app/src/main/java/org/koitharu/kotatsu/settings/utils/EditTextBindListener.kt index 7f2ccdd29..a65122501 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/utils/EditTextBindListener.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/utils/EditTextBindListener.kt @@ -6,7 +6,7 @@ import org.koitharu.kotatsu.core.util.EditTextValidator class EditTextBindListener( private val inputType: Int, - private val hint: String, + private val hint: String?, private val validator: EditTextValidator?, ) : EditTextPreference.OnBindEditTextListener { diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 612506b1d..4b0ad8866 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -53,4 +53,9 @@ @string/status_on_hold @string/status_dropped - \ No newline at end of file + + @string/disabled + HTTP + SOCKS (v4/v5) + + diff --git a/app/src/main/res/values/constants.xml b/app/src/main/res/values/constants.xml index 060c11acb..536b16a5e 100644 --- a/app/src/main/res/values/constants.xml +++ b/app/src/main/res/values/constants.xml @@ -50,4 +50,9 @@ @string/sync_host_default 86.57.183.214:8081 + + DIRECT + HTTP + SOCKS + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8cfb1c96f..df7cf2741 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -417,4 +417,8 @@ Translations WebView not available: check if WebView provider is installed Clear network cache + Type + Address + Port + Proxy diff --git a/app/src/main/res/xml/pref_content.xml b/app/src/main/res/xml/pref_content.xml index 1ecc73d25..228b40729 100644 --- a/app/src/main/res/xml/pref_content.xml +++ b/app/src/main/res/xml/pref_content.xml @@ -37,6 +37,11 @@ app:allowDividerAbove="true" app:useSimpleSummaryProvider="true" /> + + diff --git a/app/src/main/res/xml/pref_proxy.xml b/app/src/main/res/xml/pref_proxy.xml new file mode 100644 index 000000000..4bf1cad5c --- /dev/null +++ b/app/src/main/res/xml/pref_proxy.xml @@ -0,0 +1,24 @@ + + + + + + + + + +