feat(network): socks5 proxy support (#530)

This commit is contained in:
Sadwhy 2024-11-19 22:22:13 +06:00 committed by GitHub
parent a4bd367f98
commit ff3372754a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 399 additions and 7 deletions

View file

@ -157,7 +157,8 @@
android:parentActivityName=".MainActivity" />
<activity
android:name=".settings.SettingsExtensionsActivity"
android:parentActivityName=".MainActivity" />
android:parentActivityName=".MainActivity"
android:windowSoftInputMode="adjustPan"/>
<activity
android:name=".settings.SettingsAddonActivity"
android:parentActivityName=".MainActivity" />

View file

@ -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<String>(PrefName.Socks5ProxyHost) ?: ""
private var proxyPort: String? = PrefManager.getVal<String>(PrefName.Socks5ProxyPort) ?: ""
private var proxyUsername: String? = PrefManager.getVal<String>(PrefName.Socks5ProxyUsername) ?: ""
private var proxyPassword: String? = PrefManager.getVal<String>(PrefName.Socks5ProxyPassword) ?: ""
private var authEnabled: Boolean = PrefManager.getVal<Boolean>(PrefName.ProxyAuthEnabled)
private val proxyEnabled: Boolean = PrefManager.getVal<Boolean>(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()
}
}

View file

@ -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),

View file

@ -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, "")),
}

View file

@ -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<Boolean>(PrefName.EnableSocks5Proxy)
if (proxyEnabled) {
val proxyHost = PrefManager.getVal<String>(PrefName.Socks5ProxyHost)
val proxyPort = PrefManager.getVal<String>(PrefName.Socks5ProxyPort)
System.setProperty("socksProxyHost", proxyHost)
System.setProperty("socksProxyPort", proxyPort)
if (PrefManager.getVal<Boolean>(PrefName.ProxyAuthEnabled)) {
val proxyUsername = PrefManager.getVal<String>(PrefName.Socks5ProxyUsername)
val proxyPassword = PrefManager.getVal<String>(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<Boolean>(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<String>(PrefName.DefaultUserAgent)
}
}
}

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M120,880L120,600L240,600L240,440L440,440L440,360L320,360L320,80L640,80L640,360L520,360L520,440L720,440L720,600L840,600L840,880L520,880L520,600L640,600L640,520L320,520L320,600L440,600L440,880L120,880ZM400,280L560,280L560,160L400,160L400,280ZM200,800L360,800L360,680L200,680L200,800ZM600,800L760,800L760,680L600,680L600,800ZM480,280L480,280L480,280L480,280ZM360,680L360,680L360,680L360,680ZM600,680L600,680L600,680L600,680Z"/>
</vector>

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M220,880Q162,880 121,839Q80,798 80,740Q80,682 121,641Q162,600 220,600Q238,600 255,604.5Q272,609 287,617L440,464L440,354Q396,341 368,304.5Q340,268 340,220Q340,162 381,121Q422,80 480,80Q538,80 579,121Q620,162 620,220Q620,268 592,304.5Q564,341 520,354L520,464L674,617Q689,609 705.5,604.5Q722,600 740,600Q798,600 839,641Q880,682 880,740Q880,798 839,839Q798,880 740,880Q682,880 641,839Q600,798 600,740Q600,722 604.5,705Q609,688 617,673L480,536L343,673Q351,688 355.5,705Q360,722 360,740Q360,798 319,839Q278,880 220,880ZM740,800Q765,800 782.5,782.5Q800,765 800,740Q800,715 782.5,697.5Q765,680 740,680Q715,680 697.5,697.5Q680,715 680,740Q680,765 697.5,782.5Q715,800 740,800ZM480,280Q505,280 522.5,262.5Q540,245 540,220Q540,195 522.5,177.5Q505,160 480,160Q455,160 437.5,177.5Q420,195 420,220Q420,245 437.5,262.5Q455,280 480,280ZM220,800Q245,800 262.5,782.5Q280,765 280,740Q280,715 262.5,697.5Q245,680 220,680Q195,680 177.5,697.5Q160,715 160,740Q160,765 177.5,782.5Q195,800 220,800Z"/>
</vector>

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M360,720L416,664L354,600L520,600L520,520L354,520L416,456L360,400L200,560L360,720ZM600,560L760,400L600,240L544,296L606,360L440,360L440,440L606,440L544,504L600,560ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Z"/>
</vector>

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M280,720Q180,720 110,650Q40,580 40,480Q40,380 110,310Q180,240 280,240Q346,240 401,273Q456,306 488,360L920,360L920,600L840,600L840,720L600,720L600,600L488,600Q456,654 401,687Q346,720 280,720ZM280,640Q346,640 386,599.5Q426,559 434,520L680,520L680,640L760,640L760,520L840,520L840,440L434,440Q426,401 386,360.5Q346,320 280,320Q214,320 167,367Q120,414 120,480Q120,546 167,593Q214,640 280,640ZM280,560Q313,560 336.5,536.5Q360,513 360,480Q360,447 336.5,423.5Q313,400 280,400Q247,400 223.5,423.5Q200,447 200,480Q200,513 223.5,536.5Q247,560 280,560ZM280,480Q280,480 280,480Q280,480 280,480Q280,480 280,480Q280,480 280,480Q280,480 280,480Q280,480 280,480L280,480L280,480L280,480L280,480L280,480L280,480L280,480Q280,480 280,480Q280,480 280,480Z"/>
</vector>

View file

@ -0,0 +1,204 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/proxyLayoutContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bottom_sheet_background">
<LinearLayout
android:id="@+id/proxyLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="32dp"
android:visibility="visible">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:id="@+id/proxyTitle"
android:layout_width="0dp"
android:layout_height="54dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_weight="1"
android:fontFamily="@font/poppins"
android:gravity="center_vertical"
android:text="@string/socks5"
android:textSize="20sp"
android:textStyle="bold" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="16dp">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/proxyHostLayout"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="0dp"
android:layout_height="56dp"
android:layout_weight="5"
android:layout_marginEnd="8dp"
android:hint="@string/host"
app:boxCornerRadiusBottomEnd="16dp"
app:boxCornerRadiusBottomStart="16dp"
app:boxCornerRadiusTopEnd="16dp"
app:boxCornerRadiusTopStart="16dp"
app:boxStrokeColor="@color/text_input_layout_stroke_color"
app:endIconMode="none"
app:hintAnimationEnabled="true"
app:startIconDrawable="@drawable/ic_round_dns_24">
<AutoCompleteTextView
android:id="@+id/proxyHost"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:fontFamily="@font/poppins_bold"
android:inputType="textUri"
android:padding="8dp"
android:textColor="?android:attr/textColorSecondary"
android:textSize="14sp"
tools:ignore="LabelFor,TextContrastCheck" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/proxyPortLayout"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="0dp"
android:layout_height="56dp"
android:layout_weight="3"
android:hint="@string/port"
app:boxCornerRadiusBottomEnd="16dp"
app:boxCornerRadiusBottomStart="16dp"
app:boxCornerRadiusTopEnd="16dp"
app:boxCornerRadiusTopStart="16dp"
app:boxStrokeColor="@color/text_input_layout_stroke_color"
app:endIconMode="none"
app:hintAnimationEnabled="true"
app:startIconDrawable="@drawable/network_node_24">
<AutoCompleteTextView
android:id="@+id/proxyPort"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:fontFamily="@font/poppins_bold"
android:inputType="number"
android:padding="8dp"
android:textColor="?android:attr/textColorSecondary"
android:textSize="14sp"
tools:ignore="LabelFor,TextContrastCheck" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/proxyAuthentication"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/poppins_bold"
android:layout_marginTop="10dp"
android:text="@string/authentication" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/proxyUsernameLayout"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="match_parent"
android:layout_height="56dp"
android:layout_marginTop="10dp"
android:hint="@string/username"
android:enabled="false"
app:boxCornerRadiusBottomEnd="16dp"
app:boxCornerRadiusBottomStart="16dp"
app:boxCornerRadiusTopEnd="16dp"
app:boxCornerRadiusTopStart="16dp"
app:boxStrokeColor="@color/text_input_layout_stroke_color"
app:endIconMode="none"
app:hintAnimationEnabled="true"
app:startIconDrawable="@drawable/ic_round_person_24">
<AutoCompleteTextView
android:id="@+id/proxyUsername"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:fontFamily="@font/poppins_bold"
android:inputType="text"
android:padding="8dp"
android:textColor="?android:attr/textColorSecondary"
android:textSize="14sp"
android:enabled="false"
tools:ignore="LabelFor,TextContrastCheck" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/proxyPasswordLayout"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="match_parent"
android:layout_height="56dp"
android:layout_marginTop="16dp"
android:hint="@string/password"
android:enabled="false"
app:boxCornerRadiusBottomEnd="16dp"
app:boxCornerRadiusBottomStart="16dp"
app:boxCornerRadiusTopEnd="16dp"
app:boxCornerRadiusTopStart="16dp"
app:boxStrokeColor="@color/text_input_layout_stroke_color"
app:endIconMode="password_toggle"
app:hintAnimationEnabled="true"
app:startIconDrawable="@drawable/vpn_key_24">
<AutoCompleteTextView
android:id="@+id/proxyPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:fontFamily="@font/poppins_bold"
android:inputType="textPassword"
android:padding="8dp"
android:textColor="?android:attr/textColorSecondary"
android:textSize="14sp"
android:enabled="false"
tools:ignore="LabelFor,TextContrastCheck" />
</com.google.android.material.textfield.TextInputLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:orientation="horizontal">
<Button
android:id="@+id/proxySave"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="match_parent"
android:layout_height="56dp"
android:fontFamily="@font/poppins_bold"
android:insetTop="0dp"
android:insetBottom="0dp"
android:padding="8dp"
android:text="@string/save"
android:textSize="16sp"
app:cornerRadius="16dp"
app:strokeColor="?attr/colorPrimaryContainer" />
</LinearLayout>
</LinearLayout>
<androidx.cardview.widget.CardView
android:layout_width="32dp"
android:layout_height="4dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="14dp"
app:cardBackgroundColor="?attr/colorPrimary"
app:cardCornerRadius="2dp"
app:cardElevation="0dp" />
</FrameLayout>

View file

@ -1073,6 +1073,16 @@ Non quae tempore quo provident laudantium qui illo dolor vel quia dolor et exerc
<string name="private_mode">Private</string>
<string name="omega_cursed">you have been Ǫ̴̺̙͎̤̫͓̮̰̿͝M̴͇̤͗́̾̈́̑̍̿̈͌͝Ȅ̴̡̨̛͉̣̙̩̲̣̤̟̪̣̎͗̎̆̒̉͆̆̕ͅͅǴ̸̯̬̗̠̙͛͐̀̈͋̀̈̽́̎̿͘͘͝ͅĀ̶̧̲̀ͅ ̴̢̟͕̜̓̾̓C̶̬̜̰̘̝̱̫͓͙̭̈́͐͋̓̏̈̍̓̀̌̾̚Ư̸̛̤̱̈́͆̽͊͛̐̓́̑͘̕̕͝R̸̨̨͈̬̱̺͕̪̪̘͕͎̂͛́̅̆̓̀͝ͅS̴̨̨̛̩̭̗̹̰̭̥͉̮̝̠̓̔͆̂͊͆̀̈́̅̕͘̚͝È̴̢̛̝͈̳͉͈͒͒̒̄̏̈̈́D̸̢̡̨̜̞̩̼̫̹̗̮͛̀̈̋̾̇̕̕͜ͅ</string>
<string name="omega_freed">you have been freed</string>
<string name="subtitle_langauge">Subtitle Langauge</string>
<string name="socks5">SOCKS5</string>
<string name="host">Host</string>
<string name="port">Port</string>
<string name="authentication">Authentication</string>
<string name="proxy">Socks5 Proxy</string>
<string name="proxy_desc">Route All Your Network Traffic Through a Socks5 Proxy</string>
<string name="proxy_setup">Proxy Setup</string>
<string name="proxy_setup_desc">Configure your Socks5 Proxy</string>
</resources>