diff --git a/app/build.gradle b/app/build.gradle
index 4c091e12e..72fe3b5bf 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -24,6 +24,8 @@ android {
buildTypes {
debug {
+ applicationIdSuffix ".debug"
+ versionNameSuffix "-debug"
minifyEnabled !rootProject.ext.ci
useProguard false
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 3fa857c33..4d8a6aecb 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -27,12 +27,12 @@
-
-
-
-
+
+
@@ -271,15 +271,15 @@
android:name=".data.WebCacheService"
android:exported="false" />
diff --git a/app/src/main/java/io/github/hidroh/materialistic/DelegatingProxy.kt b/app/src/main/java/io/github/hidroh/materialistic/DelegatingProxy.kt
new file mode 100644
index 000000000..84220af20
--- /dev/null
+++ b/app/src/main/java/io/github/hidroh/materialistic/DelegatingProxy.kt
@@ -0,0 +1,19 @@
+package io.github.hidroh.materialistic
+
+import android.content.Context
+import java.net.InetSocketAddress
+import java.net.Proxy
+
+class DelegatingProxy : Proxy(Type.SOCKS, InetSocketAddress.createUnresolved("127.0.0.1", 1)) {
+ override fun type() = activeProxy.type()
+ override fun address() = activeProxy.address()
+
+ companion object {
+ var activeProxy: Proxy = NO_PROXY
+
+ @JvmStatic
+ fun updateFromPreferences(context: Context) {
+ activeProxy = Preferences.getProxy(context)
+ }
+ }
+}
diff --git a/app/src/main/java/io/github/hidroh/materialistic/Preferences.java b/app/src/main/java/io/github/hidroh/materialistic/Preferences.java
index 666e32d98..80ef1349d 100644
--- a/app/src/main/java/io/github/hidroh/materialistic/Preferences.java
+++ b/app/src/main/java/io/github/hidroh/materialistic/Preferences.java
@@ -23,6 +23,7 @@
import android.content.pm.PackageManager;
import android.text.TextUtils;
+import java.net.*;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
@@ -304,6 +305,21 @@ public static SwipeAction[] getListSwipePreferences(Context context) {
return new SwipeAction[]{parseSwipeAction(left), parseSwipeAction(right)};
}
+ public static Proxy getProxy(Context context) {
+ boolean isEnabled = get(context, R.string.pref_proxy_enabled, false);
+ if (!isEnabled)
+ return Proxy.NO_PROXY;
+ Proxy.Type type = getProxyType(context);
+ String host = get(context, R.string.pref_proxy_host, R.string.pref_proxy_host_default);
+ int port = Integer.parseInt(get(context, R.string.pref_proxy_port, R.string.pref_proxy_port_default));
+ try {
+ return new Proxy(type, new InetSocketAddress(InetAddress.getByName(host), port));
+ } catch (UnknownHostException e) {
+ e.printStackTrace();
+ return Proxy.NO_PROXY;
+ }
+ }
+
public static void reset(Context context) {
PreferenceManager.getDefaultSharedPreferences(context)
.edit()
@@ -311,6 +327,17 @@ public static void reset(Context context) {
.apply();
}
+ private static Proxy.Type getProxyType(Context context) {
+ String protocolStr = get(context, R.string.pref_proxy_protocol, R.string.pref_proxy_protocol_socks);
+ if (context.getResources().getString(R.string.pref_proxy_protocol_value_socks).equals(protocolStr)) {
+ return Proxy.Type.SOCKS;
+ } else if (context.getResources().getString(R.string.pref_proxy_protocol_value_http).equals(protocolStr)) {
+ return Proxy.Type.HTTP;
+ } else {
+ throw new IllegalArgumentException("Invalid proxy protocol: " + protocolStr);
+ }
+ }
+
private static SwipeAction parseSwipeAction(String value) {
try {
return SwipeAction.valueOf(value);
diff --git a/app/src/main/java/io/github/hidroh/materialistic/PreferencesActivity.java b/app/src/main/java/io/github/hidroh/materialistic/PreferencesActivity.java
index 058cc8370..898e4844e 100644
--- a/app/src/main/java/io/github/hidroh/materialistic/PreferencesActivity.java
+++ b/app/src/main/java/io/github/hidroh/materialistic/PreferencesActivity.java
@@ -48,6 +48,12 @@ protected void onCreate(Bundle savedInstanceState) {
}
}
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ DelegatingProxy.updateFromPreferences(this);
+ }
+
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
diff --git a/app/src/main/java/io/github/hidroh/materialistic/SettingsActivity.java b/app/src/main/java/io/github/hidroh/materialistic/SettingsActivity.java
index aa814f859..e7e00879b 100644
--- a/app/src/main/java/io/github/hidroh/materialistic/SettingsActivity.java
+++ b/app/src/main/java/io/github/hidroh/materialistic/SettingsActivity.java
@@ -59,6 +59,10 @@ protected void onCreate(Bundle savedInstanceState) {
startActivity(new Intent(SettingsActivity.this, PreferencesActivity.class)
.putExtra(PreferencesActivity.EXTRA_TITLE, R.string.readability)
.putExtra(PreferencesActivity.EXTRA_PREFERENCES, R.xml.preferences_readability)));
+ findViewById(R.id.menu_proxy).setOnClickListener(v ->
+ startActivity(new Intent(SettingsActivity.this, PreferencesActivity.class)
+ .putExtra(PreferencesActivity.EXTRA_TITLE, R.string.proxy)
+ .putExtra(PreferencesActivity.EXTRA_PREFERENCES, R.xml.preferences_proxy)));
findViewById(R.id.drawer_about).setOnClickListener(v ->
startActivity(new Intent(SettingsActivity.this, AboutActivity.class)));
findViewById(R.id.drawer_release).setOnClickListener(v ->
diff --git a/app/src/main/java/io/github/hidroh/materialistic/ThemedActivity.java b/app/src/main/java/io/github/hidroh/materialistic/ThemedActivity.java
index d70ae2bc7..e2d70c071 100644
--- a/app/src/main/java/io/github/hidroh/materialistic/ThemedActivity.java
+++ b/app/src/main/java/io/github/hidroh/materialistic/ThemedActivity.java
@@ -40,6 +40,11 @@ protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTaskTitle(getTitle());
mMenuTintDelegate.onActivityCreated(this);
+
+ // Maybe not the right place to do this but this needs to run
+ // after the app starts and ThemedActivity is a common base
+ // class for activities
+ DelegatingProxy.updateFromPreferences(this);
}
@Override
diff --git a/app/src/main/java/io/github/hidroh/materialistic/data/RestServiceFactory.java b/app/src/main/java/io/github/hidroh/materialistic/data/RestServiceFactory.java
index 3f8340ad0..6d2c97171 100644
--- a/app/src/main/java/io/github/hidroh/materialistic/data/RestServiceFactory.java
+++ b/app/src/main/java/io/github/hidroh/materialistic/data/RestServiceFactory.java
@@ -24,7 +24,8 @@
import javax.inject.Inject;
-import okhttp3.Call;
+import io.github.hidroh.materialistic.*;
+import okhttp3.*;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
@@ -72,6 +73,10 @@ public T create(String baseUrl, Class clazz, Executor callbackExecutor) {
builder.callFactory(mCallFactory)
.callbackExecutor(callbackExecutor != null ?
callbackExecutor : new MainThreadExecutor());
+ OkHttpClient httpClient = new OkHttpClient.Builder()
+ .proxy(new DelegatingProxy())
+ .build();
+ builder.client(httpClient);
return builder.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build()
diff --git a/app/src/main/res/drawable/ic_baseline_language_24.xml b/app/src/main/res/drawable/ic_baseline_language_24.xml
new file mode 100644
index 000000000..c70400f8e
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_language_24.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml
index 3239813ea..d9f873420 100644
--- a/app/src/main/res/layout/activity_settings.xml
+++ b/app/src/main/res/layout/activity_settings.xml
@@ -90,6 +90,17 @@
android:layout_width="match_parent"
android:layout_height="@dimen/divider" />
+
+
+
+
article
readability
+
+
+ - @string/pref_proxy_protocol_socks
+ - @string/pref_proxy_protocol_http
+
+
+ - @string/pref_proxy_protocol_value_socks
+ - @string/pref_proxy_protocol_value_http
+
+ socks
+ http
+
- @string/comment_max_lines_3
diff --git a/app/src/main/res/values/ids.xml b/app/src/main/res/values/ids.xml
index 75d0cc82e..cab92b814 100644
--- a/app/src/main/res/values/ids.xml
+++ b/app/src/main/res/values/ids.xml
@@ -63,6 +63,7 @@
+
diff --git a/app/src/main/res/values/non_translatable.xml b/app/src/main/res/values/non_translatable.xml
index d1eed3ad7..8512b766f 100644
--- a/app/src/main/res/values/non_translatable.xml
+++ b/app/src/main/res/values/non_translatable.xml
@@ -95,5 +95,7 @@
]]>
io.github.hidroh.materialistic.FabAwareScrollBehavior
+ 127.0.0.1
+ 9050
diff --git a/app/src/main/res/values/preference_keys.xml b/app/src/main/res/values/preference_keys.xml
index 62db43f68..82a6e1d69 100644
--- a/app/src/main/res/values/preference_keys.xml
+++ b/app/src/main/res/values/preference_keys.xml
@@ -66,4 +66,9 @@
pref_username
pref_volume
pref_volume_help
+ pref_proxy
+ pref_proxy_enabled
+ pref_proxy_protocol
+ pref_proxy_host
+ pref_proxy_port
\ 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 33b07e4c5..c3b790372 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -23,6 +23,7 @@
Best Stories
Comments
What\'s New
+ Proxy
Materialistic
Open drawer
Close drawer
@@ -110,6 +111,13 @@
Disable to improve scroll accuracy
Swipe left to
Swipe right to
+ Use proxy
+ Use a proxy server for all network requests not made via an external browser
+ Proxy protocol
+ SOCKS
+ HTTP
+ Proxy server host
+ Proxy server port
Unable to connect to Hacker News. Check your connection or try again.
Select a story to view
@@ -321,5 +329,6 @@
Share file
Privacy Policy
Downloads
+ Proxy
diff --git a/app/src/main/res/xml/preferences_proxy.xml b/app/src/main/res/xml/preferences_proxy.xml
new file mode 100644
index 000000000..20bd927ae
--- /dev/null
+++ b/app/src/main/res/xml/preferences_proxy.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+