From ff3372754ab90cf95d91657bf2d64bfe93abb031 Mon Sep 17 00:00:00 2001 From: Sadwhy <99601717+Sadwhy@users.noreply.github.com> Date: Tue, 19 Nov 2024 22:22:13 +0600 Subject: [PATCH] feat(network): socks5 proxy support (#530) --- app/src/main/AndroidManifest.xml | 3 +- .../dantotsu/settings/ProxyDialogFragment.kt | 71 ++++++ .../settings/SettingsExtensionsActivity.kt | 22 ++ .../dantotsu/settings/saving/Preferences.kt | 8 + .../kanade/tachiyomi/network/NetworkHelper.kt | 48 ++++- app/src/main/res/drawable/lan_24.xml | 10 + app/src/main/res/drawable/network_node_24.xml | 10 + .../drawable/swap_horizontal_circle_24.xml | 10 + app/src/main/res/drawable/vpn_key_24.xml | 10 + .../main/res/layout/bottom_sheet_proxy.xml | 204 ++++++++++++++++++ app/src/main/res/values/strings.xml | 10 + 11 files changed, 399 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/ani/dantotsu/settings/ProxyDialogFragment.kt create mode 100644 app/src/main/res/drawable/lan_24.xml create mode 100644 app/src/main/res/drawable/network_node_24.xml create mode 100644 app/src/main/res/drawable/swap_horizontal_circle_24.xml create mode 100644 app/src/main/res/drawable/vpn_key_24.xml create mode 100644 app/src/main/res/layout/bottom_sheet_proxy.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c64a42af..1d8d4d6a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -157,7 +157,8 @@ android:parentActivityName=".MainActivity" /> + android:parentActivityName=".MainActivity" + android:windowSoftInputMode="adjustPan"/> diff --git a/app/src/main/java/ani/dantotsu/settings/ProxyDialogFragment.kt b/app/src/main/java/ani/dantotsu/settings/ProxyDialogFragment.kt new file mode 100644 index 00000000..a0c275be --- /dev/null +++ b/app/src/main/java/ani/dantotsu/settings/ProxyDialogFragment.kt @@ -0,0 +1,71 @@ +package ani.dantotsu.settings + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import ani.dantotsu.BottomSheetDialogFragment +import ani.dantotsu.databinding.BottomSheetProxyBinding +import ani.dantotsu.snackString +import ani.dantotsu.settings.saving.PrefManager +import ani.dantotsu.settings.saving.PrefName +import ani.dantotsu.restartApp + +class ProxyDialogFragment : BottomSheetDialogFragment() { + private var _binding: BottomSheetProxyBinding? = null + private val binding get() = _binding!! + + private var proxyHost: String? = PrefManager.getVal(PrefName.Socks5ProxyHost) ?: "" + private var proxyPort: String? = PrefManager.getVal(PrefName.Socks5ProxyPort) ?: "" + private var proxyUsername: String? = PrefManager.getVal(PrefName.Socks5ProxyUsername) ?: "" + private var proxyPassword: String? = PrefManager.getVal(PrefName.Socks5ProxyPassword) ?: "" + private var authEnabled: Boolean = PrefManager.getVal(PrefName.ProxyAuthEnabled) + private val proxyEnabled: Boolean = PrefManager.getVal(PrefName.EnableSocks5Proxy) + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = BottomSheetProxyBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + binding.proxyHost.setText(proxyHost) + binding.proxyPort.setText(proxyPort) + binding.proxyUsername.setText(proxyUsername) + binding.proxyPassword.setText(proxyPassword) + binding.proxyAuthentication.isChecked = authEnabled + + binding.proxySave.setOnClickListener { + proxyHost = binding.proxyHost.text.toString() ?: "" + proxyPort = binding.proxyPort.text.toString() ?: "" + proxyUsername = binding.proxyUsername.text.toString() ?: "" + proxyPassword = binding.proxyPassword.text.toString() ?: "" + + PrefManager.setVal(PrefName.Socks5ProxyHost, proxyHost) + PrefManager.setVal(PrefName.Socks5ProxyPort, proxyPort) + PrefManager.setVal(PrefName.Socks5ProxyUsername, proxyUsername) + PrefManager.setVal(PrefName.Socks5ProxyPassword, proxyPassword) + + dismiss() + if (proxyEnabled) activity?.restartApp() + } + + binding.proxyAuthentication.setOnCheckedChangeListener { _, isChecked -> + PrefManager.setVal(PrefName.ProxyAuthEnabled, isChecked) + binding.proxyUsername.isEnabled = isChecked + binding.proxyPassword.isEnabled = isChecked + binding.proxyUsernameLayout.isEnabled = isChecked + binding.proxyPasswordLayout.isEnabled = isChecked + } + } + + override fun onDestroyView() { + _binding = null + super.onDestroyView() + } +} \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/settings/SettingsExtensionsActivity.kt b/app/src/main/java/ani/dantotsu/settings/SettingsExtensionsActivity.kt index a054b89a..4388231a 100644 --- a/app/src/main/java/ani/dantotsu/settings/SettingsExtensionsActivity.kt +++ b/app/src/main/java/ani/dantotsu/settings/SettingsExtensionsActivity.kt @@ -23,9 +23,11 @@ import ani.dantotsu.initActivity import ani.dantotsu.media.MediaType import ani.dantotsu.navBarHeight import ani.dantotsu.parsers.ParserTestActivity +import ani.dantotsu.restartApp import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.statusBarHeight +import ani.dantotsu.others.CustomBottomDialog import ani.dantotsu.themes.ThemeManager import ani.dantotsu.util.customAlertDialog import eu.kanade.domain.base.BasePreferences @@ -271,6 +273,26 @@ class SettingsExtensionsActivity : AppCompatActivity() { } ), Settings( + type = 2, + name = getString(R.string.proxy), + desc = getString(R.string.proxy_desc), + icon = R.drawable.swap_horizontal_circle_24, + isChecked = PrefManager.getVal(PrefName.EnableSocks5Proxy), + switch = { isChecked, _ -> + PrefManager.setVal(PrefName.EnableSocks5Proxy, isChecked) + restartApp() + } + ), + Settings( + type = 1, + name = getString(R.string.proxy_setup), + desc = getString(R.string.proxy_setup_desc), + icon = R.drawable.lan_24, + onClick = { + ProxyDialogFragment().show(supportFragmentManager, "dialog") + } + ), + Settings( type = 2, name = getString(R.string.force_legacy_installer), desc = getString(R.string.force_legacy_installer_desc), diff --git a/app/src/main/java/ani/dantotsu/settings/saving/Preferences.kt b/app/src/main/java/ani/dantotsu/settings/saving/Preferences.kt index b175abab..a2e672e1 100644 --- a/app/src/main/java/ani/dantotsu/settings/saving/Preferences.kt +++ b/app/src/main/java/ani/dantotsu/settings/saving/Preferences.kt @@ -47,6 +47,8 @@ enum class PrefName(val data: Pref) { //TODO: Split this into multiple files IncludeMangaList(Pref(Location.General, Boolean::class, true)), AdultOnly(Pref(Location.General, Boolean::class, false)), CommentsEnabled(Pref(Location.General, Int::class, 0)), + EnableSocks5Proxy(Pref(Location.General, Boolean::class, false)), + ProxyAuthEnabled(Pref(Location.General, Boolean::class, false)), //User Interface UseOLED(Pref(Location.UI, Boolean::class, false)), @@ -197,6 +199,8 @@ enum class PrefName(val data: Pref) { //TODO: Split this into multiple files RefreshStatus(Pref(Location.Irrelevant, Boolean::class, false)), rpcEnabled(Pref(Location.Irrelevant, Boolean::class, true)), + //testing + //Protected DiscordToken(Pref(Location.Protected, String::class, "")), DiscordId(Pref(Location.Protected, String::class, "")), @@ -210,4 +214,8 @@ enum class PrefName(val data: Pref) { //TODO: Split this into multiple files AppPassword(Pref(Location.Protected, String::class, "")), BiometricToken(Pref(Location.Protected, String::class, "")), OverridePassword(Pref(Location.Protected, Boolean::class, false)), + Socks5ProxyHost(Pref(Location.Protected, String::class, "")), + Socks5ProxyPort(Pref(Location.Protected, String::class, "")), + Socks5ProxyUsername(Pref(Location.Protected, String::class, "")), + Socks5ProxyPassword(Pref(Location.Protected, String::class, "")), } \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/network/NetworkHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/network/NetworkHelper.kt index ef9855a8..189c5663 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/network/NetworkHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/network/NetworkHelper.kt @@ -17,11 +17,50 @@ import okhttp3.brotli.BrotliInterceptor import okhttp3.logging.HttpLoggingInterceptor import java.io.File import java.util.concurrent.TimeUnit +import java.net.InetSocketAddress +import java.net.Proxy +import java.net.Authenticator +import java.net.PasswordAuthentication +import java.util.prefs.Preferences +import okhttp3.Credentials +import okhttp3.Response +import okhttp3.Route class NetworkHelper( context: Context ) { + init { + setupSocks5Proxy() + } + +private fun setupSocks5Proxy() { + val proxyEnabled = PrefManager.getVal(PrefName.EnableSocks5Proxy) + if (proxyEnabled) { + val proxyHost = PrefManager.getVal(PrefName.Socks5ProxyHost) + val proxyPort = PrefManager.getVal(PrefName.Socks5ProxyPort) + + System.setProperty("socksProxyHost", proxyHost) + System.setProperty("socksProxyPort", proxyPort) + + if (PrefManager.getVal(PrefName.ProxyAuthEnabled)) { + val proxyUsername = PrefManager.getVal(PrefName.Socks5ProxyUsername) + val proxyPassword = PrefManager.getVal(PrefName.Socks5ProxyPassword) + + Authenticator.setDefault(object : Authenticator() { + override fun getPasswordAuthentication(): PasswordAuthentication { + return PasswordAuthentication(proxyUsername, proxyPassword.toCharArray()) + } + } + ) + } + } else { + System.clearProperty("socksProxyHost") + System.clearProperty("socksProxyPort") + Authenticator.setDefault(null) + } + } + val cookieJar = AndroidCookieJar() val client: OkHttpClient = run { @@ -47,11 +86,9 @@ class NetworkHelper( } } - if (PrefManager.getVal(PrefName.VerboseLogging)) { val httpLoggingInterceptor = HttpLoggingInterceptor(ConsoleLogger()).apply { level = HttpLoggingInterceptor.Level.BASIC - } builder.addNetworkInterceptor(httpLoggingInterceptor) } @@ -75,9 +112,8 @@ class NetworkHelper( PREF_DOH_SHECAN -> builder.dohShecan() PREF_DOH_LIBREDNS -> builder.dohLibreDNS() } - - builder.build() - } + builder.build() + } val downloadClient = client.newBuilder().callTimeout(20, TimeUnit.MINUTES).build() @@ -103,4 +139,4 @@ class NetworkHelper( companion object { fun defaultUserAgentProvider() = PrefManager.getVal(PrefName.DefaultUserAgent) } -} +} \ No newline at end of file diff --git a/app/src/main/res/drawable/lan_24.xml b/app/src/main/res/drawable/lan_24.xml new file mode 100644 index 00000000..dc99da69 --- /dev/null +++ b/app/src/main/res/drawable/lan_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/network_node_24.xml b/app/src/main/res/drawable/network_node_24.xml new file mode 100644 index 00000000..267173b5 --- /dev/null +++ b/app/src/main/res/drawable/network_node_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/swap_horizontal_circle_24.xml b/app/src/main/res/drawable/swap_horizontal_circle_24.xml new file mode 100644 index 00000000..15a2fc5b --- /dev/null +++ b/app/src/main/res/drawable/swap_horizontal_circle_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/vpn_key_24.xml b/app/src/main/res/drawable/vpn_key_24.xml new file mode 100644 index 00000000..cef95237 --- /dev/null +++ b/app/src/main/res/drawable/vpn_key_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout/bottom_sheet_proxy.xml b/app/src/main/res/layout/bottom_sheet_proxy.xml new file mode 100644 index 00000000..59ea6d55 --- /dev/null +++ b/app/src/main/res/layout/bottom_sheet_proxy.xml @@ -0,0 +1,204 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +