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 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index fbc12df8..8a2217ac 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1073,6 +1073,16 @@ Non quae tempore quo provident laudantium qui illo dolor vel quia dolor et exerc
Private
you have been Ǫ̴̺̙͎̤̫͓̮̰̿͝M̴͇̤͗́̾̈́̑̍̿̈͌͝Ȅ̴̡̨̛͉̣̙̩̲̣̤̟̪̣̎͗̎̆̒̉͆̆̕ͅͅǴ̸̯̬̗̠̙͛͐̀̈͋̀̈̽́̎̿͘͘͝ͅĀ̶̧̲̀ͅ ̴̢̟͕̜̓̾̓C̶̬̜̰̘̝̱̫͓͙̭̈́͐͋̓̏̈̍̓̀̌̾̚Ư̸̛̤̱̈́͆̽͊͛̐̓́̑͘̕̕͝R̸̨̨͈̬̱̺͕̪̪̘͕͎̂͛́̅̆̓̀͝ͅS̴̨̨̛̩̭̗̹̰̭̥͉̮̝̠̓̔͆̂͊͆̀̈́̅̕͘̚͝È̴̢̛̝͈̳͉͈͒͒̒̄̏̈̈́D̸̢̡̨̜̞̩̼̫̹̗̮͛̀̈̋̾̇̕̕͜ͅ
you have been freed
+
Subtitle Langauge
+ SOCKS5
+ Host
+ Port
+ Authentication
+ Socks5 Proxy
+ Route All Your Network Traffic Through a Socks5 Proxy
+ Proxy Setup
+ Configure your Socks5 Proxy
+