Add option to use socks proxy for orbot (closes bitfireAT/davx5#28) (#64)

* Add option to use SOCKS proxy for Orbot
* Fix tests, add constants
* Add option for "no proxy", add migration
* Improve UI

Co-authored-by: Ricki Hirner <hirner@bitfire.at>
This commit is contained in:
Sunik KupferandRicki Hirner authored and Ricki Hirner committed 2022-03-09 12:51:31 +01:00
1 parent 8fb9b3a86e
commit 7367477ebe
8 files changed
+130 -58

No files matched your search

@@ -5,8 +5,8 @@
package at.bitfire.davdroid.settings
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
class SettingsManagerTest {
@@ -14,11 +14,14 @@ class SettingsManagerTest {
val settingsManager by lazy { SettingsManager.getInstance(InstrumentationRegistry.getInstrumentation().targetContext) }
@Test
fun testContainsKey() {
fun testContainsKey_NotExisting() {
assertFalse(settingsManager.containsKey("notExisting"))
}
@Test
fun testContainsKey_Existing() {
// provided by DefaultsProvider
assertTrue(settingsManager.containsKey(Settings.OVERRIDE_PROXY))
assertEquals(Settings.PROXY_TYPE_SYSTEM, settingsManager.getInt(Settings.PROXY_TYPE))
}
}
@@ -103,15 +103,24 @@ class HttpClient private constructor(
// custom proxy support
try {
if (settings.getBoolean(Settings.OVERRIDE_PROXY)) {
val address = InetSocketAddress(
settings.getString(Settings.OVERRIDE_PROXY_HOST),
settings.getInt(Settings.OVERRIDE_PROXY_PORT)
)
val proxy = Proxy(Proxy.Type.HTTP, address)
val proxyTypeValue = settings.getInt(Settings.PROXY_TYPE)
if (proxyTypeValue != Settings.PROXY_TYPE_SYSTEM) {
// we set our own proxy
val address by lazy { // lazy because not required for PROXY_TYPE_NONE
InetSocketAddress(
settings.getString(Settings.PROXY_HOST),
settings.getInt(Settings.PROXY_PORT)
)
}
val proxy =
when (proxyTypeValue) {
Settings.PROXY_TYPE_NONE -> Proxy.NO_PROXY
Settings.PROXY_TYPE_HTTP -> Proxy(Proxy.Type.HTTP, address)
Settings.PROXY_TYPE_SOCKS -> Proxy(Proxy.Type.SOCKS, address)
else -> throw IllegalArgumentException("Invalid proxy type")
}
orig.proxy(proxy)
Logger.log.log(Level.INFO, "Using proxy", proxy)
Logger.log.log(Level.INFO, "Using proxy setting", proxy)
}
} catch (e: Exception) {
Logger.log.log(Level.SEVERE, "Can't set proxy, ignoring", e)
@@ -20,6 +20,7 @@ import android.provider.ContactsContract
import android.util.Base64
import androidx.annotation.WorkerThread
import androidx.core.content.ContextCompat
import androidx.preference.PreferenceManager
import at.bitfire.davdroid.InvalidAccountException
import at.bitfire.davdroid.R
import at.bitfire.davdroid.TasksWatcher
@@ -62,7 +63,7 @@ class AccountSettings(
companion object {
const val CURRENT_VERSION = 12
const val CURRENT_VERSION = 13
const val KEY_SETTINGS_VERSION = "version"
const val KEY_SYNC_INTERVAL_ADDRESSBOOKS = "sync_interval_addressbooks"
@@ -429,6 +430,45 @@ class AccountSettings(
}
@Suppress("unused","FunctionName")
/**
* Not a per-account migration, but not a database migration, too, so it fits best there.
* Best future solution would be that SettingsManager manages versions and migrations.
*
* Updates proxy settings from override_proxy_* to proxy_type, proxy_host, proxy_port.
*/
private fun update_12_13() {
// proxy settings are managed by SharedPreferencesProvider
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
// old setting names
val overrideProxy = "override_proxy"
val overrideProxyHost = "override_proxy_host"
val overrideProxyPort = "override_proxy_port"
val edit = preferences.edit()
if (preferences.contains(overrideProxy)) {
if (preferences.getBoolean(overrideProxy, false))
// override_proxy set, migrate to proxy_type = HTTP
edit.putInt(Settings.PROXY_TYPE, Settings.PROXY_TYPE_HTTP)
edit.remove(overrideProxy)
}
if (preferences.contains(overrideProxyHost)) {
preferences.getString(overrideProxyHost, null)?.let { host ->
edit.putString(Settings.PROXY_HOST, host)
}
edit.remove(overrideProxyHost)
}
if (preferences.contains(overrideProxyPort)) {
val port = preferences.getInt(overrideProxyPort, 0)
if (port != 0)
edit.putInt(Settings.PROXY_PORT, port)
edit.remove(overrideProxyPort)
}
edit.apply()
}
@Suppress("unused","FunctionName")
/**
* Store event URLs as URL (extended property) instead of unknown property. At the same time,
@@ -19,19 +19,19 @@ class DefaultsProvider(
override val booleanDefaults = mutableMapOf(
Pair(Settings.DISTRUST_SYSTEM_CERTIFICATES, false),
Pair(Settings.OVERRIDE_PROXY, false)
)
override val intDefaults = mapOf(
Pair(Settings.OVERRIDE_PROXY_PORT, 8118)
Pair(Settings.PROXY_TYPE, Settings.PROXY_TYPE_SYSTEM),
Pair(Settings.PROXY_PORT, 9050) // Orbot SOCKS
)
override val longDefaults = mapOf<String, Long>(
Pair(Settings.DEFAULT_SYNC_INTERVAL, 4*3600) /* 4 hours */
Pair(Settings.DEFAULT_SYNC_INTERVAL, 4*3600) /* 4 hours */
)
override val stringDefaults = mapOf(
Pair(Settings.OVERRIDE_PROXY_HOST, "localhost")
Pair(Settings.PROXY_HOST, "localhost")
)
val dataSaverChangedListener by lazy {
@@ -13,9 +13,13 @@ object Settings {
const val DISTRUST_SYSTEM_CERTIFICATES = "distrust_system_certs"
const val OVERRIDE_PROXY = "override_proxy"
const val OVERRIDE_PROXY_HOST = "override_proxy_host"
const val OVERRIDE_PROXY_PORT = "override_proxy_port"
const val PROXY_TYPE = "proxy_type"
const val PROXY_TYPE_SYSTEM = -1
const val PROXY_TYPE_NONE = 0
const val PROXY_TYPE_HTTP = 1
const val PROXY_TYPE_SOCKS = 2
const val PROXY_HOST = "proxy_host"
const val PROXY_PORT = "proxy_port"
/**
* Default sync interval (long), in seconds.
@@ -138,44 +138,50 @@ class AppSettingsActivity: AppCompatActivity() {
}
// connection settings
findPreference<SwitchPreferenceCompat>(Settings.OVERRIDE_PROXY)!!.apply {
isChecked = settings.getBoolean(Settings.OVERRIDE_PROXY)
isEnabled = settings.isWritable(Settings.OVERRIDE_PROXY)
val proxyType = settings.getInt(Settings.PROXY_TYPE)
findPreference<ListPreference>(Settings.PROXY_TYPE)!!.apply {
setValueIndex(entryValues.indexOf(proxyType.toString()))
summary = entry
onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
settings.putBoolean(Settings.OVERRIDE_PROXY, newValue as Boolean)
val proxyType = (newValue as String).toInt()
settings.putInt(Settings.PROXY_TYPE, proxyType)
false
}
}
findPreference<EditTextPreference>(Settings.OVERRIDE_PROXY_HOST)!!.apply {
isEnabled = settings.isWritable(Settings.OVERRIDE_PROXY_HOST)
val proxyHost = settings.getString(Settings.OVERRIDE_PROXY_HOST)
findPreference<EditTextPreference>(Settings.PROXY_HOST)!!.apply {
isVisible = proxyType != Settings.PROXY_TYPE_SYSTEM && proxyType != Settings.PROXY_TYPE_NONE
isEnabled = settings.isWritable(Settings.PROXY_HOST)
val proxyHost = settings.getString(Settings.PROXY_HOST)
text = proxyHost
summary = proxyHost
onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
val host = newValue as String
try {
URI(null, host, null, null)
settings.putString(Settings.OVERRIDE_PROXY_HOST, host)
settings.putString(Settings.PROXY_HOST, host)
summary = host
false
} catch(e: URISyntaxException) {
Snackbar.make(requireView(), e.localizedMessage, Snackbar.LENGTH_LONG).show()
Snackbar.make(requireView(), e.reason, Snackbar.LENGTH_LONG).show()
false
}
}
}
findPreference<EditTextPreference>(Settings.OVERRIDE_PROXY_PORT)!!.apply {
isEnabled = settings.isWritable(Settings.OVERRIDE_PROXY_PORT)
val proxyPort = settings.getInt(Settings.OVERRIDE_PROXY_PORT)
findPreference<EditTextPreference>(Settings.PROXY_PORT)!!.apply {
isVisible = proxyType != Settings.PROXY_TYPE_SYSTEM && proxyType != Settings.PROXY_TYPE_NONE
isEnabled = settings.isWritable(Settings.PROXY_PORT)
val proxyPort = settings.getInt(Settings.PROXY_PORT)
text = proxyPort.toString()
summary = proxyPort.toString()
onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
try {
val port = Integer.parseInt(newValue as String)
val port = (newValue as String).toInt()
if (port in 1..65535) {
settings.putInt(Settings.OVERRIDE_PROXY_PORT, port)
settings.putInt(Settings.PROXY_PORT, port)
text = port.toString()
summary = port.toString()
false
@@ -195,7 +201,7 @@ class AppSettingsActivity: AppCompatActivity() {
findPreference<ListPreference>(Settings.PREFERRED_THEME)!!.apply {
val mode = settings.getIntOrNull(Settings.PREFERRED_THEME) ?: Settings.PREFERRED_THEME_DEFAULT
setValueIndex(entryValues.indexOf(mode.toString()))
summary = getString(R.string.app_settings_theme_summary, entry)
summary = entry
onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
val newMode = (newValue as String).toInt()
+17 -8
View File
@@ -163,16 +163,26 @@
<string name="app_settings_logging_on">Logging is active</string>
<string name="app_settings_logging_off">Logging is disabled</string>
<string name="app_settings_battery_optimization">Battery optimization</string>
<string name="app_settings_battery_optimization_whitelisted">DAVx5 is whitelisted (recommended)</string>
<string name="app_settings_battery_optimization_not_whitelisted">DAVx5 is not whitelisted (not recommended)</string>
<string name="app_settings_battery_optimization_whitelisted">App is whitelisted (recommended)</string>
<string name="app_settings_battery_optimization_not_whitelisted">App is not whitelisted (not recommended)</string>
<string name="app_settings_foreground_service">Keep in foreground</string>
<string name="app_settings_foreground_service_details">May help if your device prevents automatic synchronization</string>
<string name="app_settings_connection">Connection</string>
<string name="app_settings_override_proxy">Override proxy settings</string>
<string name="app_settings_override_proxy_on">Use custom proxy settings</string>
<string name="app_settings_override_proxy_off">Use system default proxy settings</string>
<string name="app_settings_override_proxy_host">HTTP proxy host name</string>
<string name="app_settings_override_proxy_port">HTTP proxy port</string>
<string name="app_settings_proxy">Proxy type</string>
<string-array name="app_settings_proxy_types">
<item>System default</item>
<item>No proxy</item>
<item>HTTP</item>
<item>SOCKS (for Orbot)</item>
</string-array>
<string-array name="app_settings_proxy_type_values" translatable="false">
<item>-1</item>
<item>0</item>
<item>1</item>
<item>2</item>
</string-array>
<string name="app_settings_proxy_host">Proxy host name</string>
<string name="app_settings_proxy_port">Proxy port</string>
<string name="app_settings_security">Security</string>
<string name="app_settings_security_app_permissions">App permissions</string>
<string name="app_settings_security_app_permissions_summary">Review permissions required for synchronization</string>
@@ -186,7 +196,6 @@
<string name="app_settings_notification_settings">Notification settings</string>
<string name="app_settings_notification_settings_summary">Manage notification channels and their settings</string>
<string name="app_settings_theme_title">Select theme</string>
<string name="app_settings_theme_summary">Current theme: %s</string>
<string-array name="app_settings_theme_names">
<item>System default</item>
<item>Light</item>
+17 -16
View File
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<androidx.preference.PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="@string/app_settings_debug">
@@ -34,23 +35,24 @@
<PreferenceCategory android:title="@string/app_settings_connection">
<SwitchPreferenceCompat
android:key="override_proxy"
android:title="@string/app_settings_override_proxy"
android:summaryOn="@string/app_settings_override_proxy_on"
android:summaryOff="@string/app_settings_override_proxy_off"/>
<ListPreference
android:key="proxy_type"
android:title="@string/app_settings_proxy"
android:entries="@array/app_settings_proxy_types"
android:entryValues="@array/app_settings_proxy_type_values"
android:persistent="false" />
<EditTextPreference
android:key="override_proxy_host"
android:dependency="override_proxy"
android:title="@string/app_settings_override_proxy_host"
android:inputType="textUri"/>
android:key="proxy_host"
android:title="@string/app_settings_proxy_host"
android:inputType="textUri"
android:persistent="false" />
<at.bitfire.davdroid.ui.widget.IntEditTextPreference
android:key="override_proxy_port"
android:dependency="override_proxy"
android:title="@string/app_settings_override_proxy_port"
android:inputType="number"/>
android:key="proxy_port"
android:title="@string/app_settings_proxy_port"
android:inputType="number"
android:persistent="false" />
</PreferenceCategory>
@@ -60,7 +62,7 @@
android:key="distrust_system_certs"
android:title="@string/app_settings_distrust_system_certs"
android:summaryOn="@string/app_settings_distrust_system_certs_on"
android:summaryOff="@string/app_settings_distrust_system_certs_off"/>
android:summaryOff="@string/app_settings_distrust_system_certs_off" />
<Preference
android:key="reset_certificates"
@@ -89,7 +91,6 @@
android:key="preferred_theme"
android:icon="@drawable/ic_invert_colors"
android:title="@string/app_settings_theme_title"
android:summary="@string/app_settings_theme_summary"
android:entries="@array/app_settings_theme_names"
android:entryValues="@array/app_settings_theme_values"
android:persistent="false" />