diff --git a/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java b/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java index 06d0529f..1bb2c57d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java @@ -137,8 +137,10 @@ public abstract class BaseActivity extends AppCompatActivity { .registerTypeAdapter(Spanned.class, new SpannedTypeAdapter()) .create(); + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); + OkHttpClient.Builder okBuilder = - OkHttpUtils.getCompatibleClientBuilder() + OkHttpUtils.getCompatibleClientBuilder(preferences) .addInterceptor(new AuthInterceptor(this)) .dispatcher(mastodonApiDispatcher); diff --git a/app/src/main/java/com/keylesspalace/tusky/LoginActivity.java b/app/src/main/java/com/keylesspalace/tusky/LoginActivity.java index 0c8c0a52..4449fc46 100644 --- a/app/src/main/java/com/keylesspalace/tusky/LoginActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/LoginActivity.java @@ -152,7 +152,7 @@ public class LoginActivity extends AppCompatActivity { private MastodonApi getApiFor(String domain) { Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://" + domain) - .client(OkHttpUtils.getCompatibleClient()) + .client(OkHttpUtils.getCompatibleClient(preferences)) .addConverterFactory(GsonConverterFactory.create()) .build(); diff --git a/app/src/main/java/com/keylesspalace/tusky/NotificationPullJobCreator.java b/app/src/main/java/com/keylesspalace/tusky/NotificationPullJobCreator.java index 4fe10bc8..3b35255d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/NotificationPullJobCreator.java +++ b/app/src/main/java/com/keylesspalace/tusky/NotificationPullJobCreator.java @@ -76,8 +76,10 @@ public final class NotificationPullJobCreator implements JobCreator { } private static MastodonApi createMastodonApi(String domain, Context context) { + SharedPreferences preferences = context.getSharedPreferences( + context.getString(R.string.preferences_file_key), Context.MODE_PRIVATE); - OkHttpClient okHttpClient = OkHttpUtils.getCompatibleClientBuilder() + OkHttpClient okHttpClient = OkHttpUtils.getCompatibleClientBuilder(preferences) .addInterceptor(new AuthInterceptor(context)) .build(); diff --git a/app/src/main/java/com/keylesspalace/tusky/TuskyApplication.java b/app/src/main/java/com/keylesspalace/tusky/TuskyApplication.java index fee6dc8b..abcaecad 100644 --- a/app/src/main/java/com/keylesspalace/tusky/TuskyApplication.java +++ b/app/src/main/java/com/keylesspalace/tusky/TuskyApplication.java @@ -17,6 +17,8 @@ package com.keylesspalace.tusky; import android.app.Application; import android.arch.persistence.room.Room; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; import android.support.v7.app.AppCompatDelegate; import com.evernote.android.job.JobManager; @@ -37,7 +39,8 @@ public class TuskyApplication extends Application { super.onCreate(); // Initialize Picasso configuration Picasso.Builder builder = new Picasso.Builder(this); - builder.downloader(new OkHttp3Downloader(OkHttpUtils.getCompatibleClient())); + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); + builder.downloader(new OkHttp3Downloader(OkHttpUtils.getCompatibleClient(preferences))); if (BuildConfig.DEBUG) { builder.listener((picasso, uri, exception) -> exception.printStackTrace()); } diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/PreferencesFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/PreferencesFragment.java index 9e96d5ac..7fab0c39 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/PreferencesFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/PreferencesFragment.java @@ -16,8 +16,10 @@ package com.keylesspalace.tusky.fragment; import android.content.Intent; +import android.content.SharedPreferences; import android.os.Build; import android.os.Bundle; +import android.preference.EditTextPreference; import android.preference.Preference; import android.preference.PreferenceFragment; import android.support.annotation.XmlRes; @@ -27,7 +29,10 @@ import com.keylesspalace.tusky.PreferencesActivity; import com.keylesspalace.tusky.R; import com.keylesspalace.tusky.util.NotificationManager; -public class PreferencesFragment extends PreferenceFragment { +public class PreferencesFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener { + SharedPreferences sharedPreferences; + static boolean httpProxyChanged = false; + static boolean pendingRestart = false; public static PreferencesFragment newInstance(@XmlRes int preference) { PreferencesFragment fragment = new PreferencesFragment(); @@ -101,6 +106,91 @@ public class PreferencesFragment extends PreferenceFragment { }); } + Preference httpProxyPreferences = findPreference("httpProxyPreferences"); + if(httpProxyPreferences != null) { + httpProxyPreferences.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + PreferencesActivity activity = (PreferencesActivity) getActivity(); + if (activity != null) { + pendingRestart = false; + activity.showFragment(R.xml.http_proxy_preferences, R.string.pref_title_http_proxy_settings); + } + + return true; + } + }); + } + } + @Override + public void onResume() { + super.onResume(); + + sharedPreferences = getPreferenceManager().getSharedPreferences(); + sharedPreferences.registerOnSharedPreferenceChangeListener(this); + + updateSummary("httpProxyServer"); + updateSummary("httpProxyPort"); + updateHttpProxySummary(); + } + + @Override + public void onPause() { + sharedPreferences.unregisterOnSharedPreferenceChangeListener(this); + super.onPause(); + if (pendingRestart) { + pendingRestart = false; + httpProxyChanged = false; + System.exit(0); + } + } + + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, + String key) { + switch (key) { + case "httpProxyServer": + case "httpProxyPort": + updateSummary(key); + case "httpProxyEnabled": + httpProxyChanged = true; + break; + default: + } + } + + private void updateSummary(String key) { + switch (key) { + case "httpProxyServer": + case "httpProxyPort": + EditTextPreference editTextPreference = (EditTextPreference) findPreference(key); + if (editTextPreference != null) { + editTextPreference.setSummary(editTextPreference.getText()); + } + break; + default: + } + } + + private void updateHttpProxySummary() { + Preference httpProxyPref = findPreference("httpProxyPreferences"); + if (httpProxyPref != null) { + if (httpProxyChanged) { + pendingRestart = true; + } + + Boolean httpProxyEnabled = sharedPreferences.getBoolean("httpProxyEnabled", false); + + String httpServer = sharedPreferences.getString("httpProxyServer", ""); + int httpPort = Integer.parseInt(sharedPreferences.getString("httpProxyPort", "-1")); + + if (httpProxyEnabled && !httpServer.isEmpty() && (httpPort > 0 && httpPort < 65535)) { + httpProxyPref.setSummary(httpServer + ":" + httpPort); + } else { + httpProxyPref.setSummary(""); + } + } + } } diff --git a/app/src/main/java/com/keylesspalace/tusky/util/OkHttpUtils.java b/app/src/main/java/com/keylesspalace/tusky/util/OkHttpUtils.java index 9f4fb436..0873b218 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/OkHttpUtils.java +++ b/app/src/main/java/com/keylesspalace/tusky/util/OkHttpUtils.java @@ -15,7 +15,9 @@ package com.keylesspalace.tusky.util; +import android.content.SharedPreferences; import android.os.Build; +import android.preference.PreferenceManager; import android.support.annotation.NonNull; import android.util.Log; @@ -23,6 +25,8 @@ import com.keylesspalace.tusky.BuildConfig; import java.io.IOException; import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Proxy; import java.net.Socket; import java.security.KeyManagementException; import java.security.KeyStore; @@ -62,7 +66,11 @@ public class OkHttpUtils { * TLS 1.1 and 1.2 have to be manually enabled on API levels 16-20. */ @NonNull - public static OkHttpClient.Builder getCompatibleClientBuilder() { + public static OkHttpClient.Builder getCompatibleClientBuilder(SharedPreferences preferences) { + boolean httpProxyEnabled = preferences.getBoolean("httpProxyEnabled", false); + String httpServer = preferences.getString("httpProxyServer", ""); + int httpPort = Integer.parseInt(preferences.getString("httpProxyPort", "-1")); + ConnectionSpec fallback = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) .allEnabledCipherSuites() .supportsTlsExtensions(true) @@ -80,12 +88,17 @@ public class OkHttpUtils { .writeTimeout(30, TimeUnit.SECONDS) .connectionSpecs(specList); + if (httpProxyEnabled && !httpServer.isEmpty() && (httpPort > 0) && (httpPort < 65535)) { + InetSocketAddress address = InetSocketAddress.createUnresolved(httpServer, httpPort); + builder.proxy(new Proxy(Proxy.Type.HTTP, address)); + } + return enableHigherTlsOnPreLollipop(builder); } @NonNull - public static OkHttpClient getCompatibleClient() { - return getCompatibleClientBuilder().build(); + public static OkHttpClient getCompatibleClient(SharedPreferences preferences) { + return getCompatibleClientBuilder(preferences).build(); } /** diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d90719fc..b2c20f6d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -177,6 +177,11 @@ Show boosts Show replies Show media previews + Proxy + HTTP proxy + Enable HTTP proxy + HTTP proxy server + HTTP proxy port 15 minutes diff --git a/app/src/main/res/xml/http_proxy_preferences.xml b/app/src/main/res/xml/http_proxy_preferences.xml new file mode 100644 index 00000000..05183535 --- /dev/null +++ b/app/src/main/res/xml/http_proxy_preferences.xml @@ -0,0 +1,18 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index d9429214..d005a25c 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -74,4 +74,12 @@ android:title="@string/pref_title_edit_notification_settings" /> + + + + +