From 61c92349aad222e773171871872a222dd6189a36 Mon Sep 17 00:00:00 2001 From: vanitasvitae Date: Thu, 9 Jun 2016 01:03:50 +0200 Subject: [PATCH 1/3] Added option to set manual proxy --- app/build.gradle | 1 + .../activity/MainActivity.java | 107 +++++++++++++++++- .../diaspora_android/data/AppSettings.java | 40 +++++++ .../res/layout/proxy_configuration_dialog.xml | 52 +++++++++ app/src/main/res/values/strings.xml | 6 + 5 files changed, 204 insertions(+), 2 deletions(-) create mode 100644 app/src/main/res/layout/proxy_configuration_dialog.xml diff --git a/app/build.gradle b/app/build.gradle index a2e167e4..f8aee685 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -37,6 +37,7 @@ dependencies { // More libraries compile 'com.getbase:floatingactionbutton:1.9.1' compile 'com.jakewharton:butterknife:8.0.1' + compile 'info.guardianproject.netcipher:netcipher:1.2.1' apt 'com.jakewharton:butterknife-compiler:8.0.1' } diff --git a/app/src/main/java/com/github/dfa/diaspora_android/activity/MainActivity.java b/app/src/main/java/com/github/dfa/diaspora_android/activity/MainActivity.java index f4a64846..04240c73 100644 --- a/app/src/main/java/com/github/dfa/diaspora_android/activity/MainActivity.java +++ b/app/src/main/java/com/github/dfa/diaspora_android/activity/MainActivity.java @@ -23,7 +23,9 @@ import android.Manifest; import android.animation.ObjectAnimator; import android.annotation.SuppressLint; import android.app.Activity; +import android.app.AlarmManager; import android.app.AlertDialog; +import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; @@ -32,9 +34,11 @@ import android.content.IntentFilter; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.net.Uri; +import android.os.AsyncTask; import android.os.Bundle; import android.os.Environment; import android.os.Handler; +import android.os.StrictMode; import android.provider.MediaStore; import android.support.annotation.NonNull; import android.support.design.widget.CollapsingToolbarLayout; @@ -46,6 +50,7 @@ import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.ActionMenuView; +import android.support.v7.widget.SwitchCompat; import android.support.v7.widget.Toolbar; import android.text.Html; import android.text.SpannableString; @@ -66,6 +71,7 @@ import android.webkit.WebChromeClient; import android.webkit.WebSettings; import android.webkit.WebView; import android.widget.EditText; +import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ProgressBar; @@ -97,6 +103,8 @@ import java.util.Locale; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; +import info.guardianproject.netcipher.NetCipher; +import info.guardianproject.netcipher.web.WebkitProxy; public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener, WebUserProfileChangedListener { @@ -216,6 +224,11 @@ public class MainActivity extends AppCompatActivity if (android.os.Build.VERSION.SDK_INT >= 21) webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); + //Set proxy + if(appSettings.isProxyEnabled()) { + if(!setProxy()) Toast.makeText(this, R.string.toast_set_proxy_failed, Toast.LENGTH_LONG).show(); + } + /* * WebViewClient */ @@ -885,7 +898,7 @@ public class MainActivity extends AppCompatActivity case R.id.nav_aspects: { if (Helpers.isOnline(MainActivity.this)) { - // webView.loadUrl("https://" + podDomain + "/aspects"); + // webView.loadUrl("https://" + podDomain + "/aspects"); Helpers.showAspectList(webView, app); setTitle(R.string.title_aspects); } else { @@ -946,7 +959,8 @@ public class MainActivity extends AppCompatActivity case R.id.nav_settings_app: { final CharSequence[] options = {getString(R.string.settings_font), getString(R.string.settings_view), appSettings.isLoadImages() ? - getString(R.string.settings_images_switch_off) : getString(R.string.settings_images_switch_on), getString(R.string.jb_pod)}; + getString(R.string.settings_images_switch_off) : getString(R.string.settings_images_switch_on), getString(R.string.jb_pod), + getString(R.string.settings_proxy)}; if (Helpers.isOnline(MainActivity.this)) { new AlertDialog.Builder(MainActivity.this) @@ -979,6 +993,33 @@ public class MainActivity extends AppCompatActivity }) .show(); break; + case 4: + final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(MainActivity.this); + final View dialogLayout = getLayoutInflater().inflate(R.layout.proxy_configuration_dialog, null); + final SwitchCompat enabled_toggle = (SwitchCompat) dialogLayout.findViewById(R.id.proxy_toggle); + final EditText host_edit = (EditText) dialogLayout.findViewById(R.id.proxy_host_edit); + final EditText port_edit = (EditText) dialogLayout.findViewById(R.id.proxy_port_edit); + + enabled_toggle.setChecked(appSettings.isProxyEnabled()); + host_edit.setText(appSettings.getProxyHost()); + port_edit.setText(""+appSettings.getProxyPort()); + dialogBuilder.setView(dialogLayout).setTitle(R.string.settings_proxy) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + boolean proxyEnabled = enabled_toggle.isChecked(); + String host = host_edit.getText().toString(); + int port = Integer.parseInt(port_edit.getText().toString()); + + if(proxyEnabled) { + if(!setProxy(host, port)) { + Toast.makeText(MainActivity.this, R.string.toast_set_proxy_failed,Toast.LENGTH_SHORT).show(); + } + } + else resetProxy(); + } + }).setNegativeButton(android.R.string.cancel, null).show(); + break; } } }).show(); @@ -1069,4 +1110,66 @@ public class MainActivity extends AppCompatActivity grantResults); } } + + /** + * Set proxy according to arguments. host must not be "" or null, port must be positive. + * Return true on success and update appSettings' proxy related values. + * @param host proxy host (eg. localhost or 127.0.0.1) + * @param port proxy port (eg. 8118) + * @return success + * @throws IllegalArgumentException if arguments do not fit specifications above + */ + private boolean setProxy(final String host, final int port) { + if(host != null && !host.equals("") && port >=0) { + //Temporary change thread policy + StrictMode.ThreadPolicy old = StrictMode.getThreadPolicy(); + StrictMode.ThreadPolicy tmp = new StrictMode.ThreadPolicy.Builder().permitAll().build(); + StrictMode.setThreadPolicy(tmp); + + NetCipher.setProxy(host, port); //Proxy for HttpsUrlConnections + try { + //Proxy for the webview + WebkitProxy.setProxy(MainActivity.class.getName(), getApplicationContext(), null, host, port); + } catch (Exception e) { /*Nothing we can do*/ } + + appSettings.setProxyEnabled(true); + appSettings.setProxyHost(host); + appSettings.setProxyPort(port); + + StrictMode.setThreadPolicy(old); + webView.reload(); + return true; + } else { + return false; + } + } + + private boolean setProxy() { + return setProxy(appSettings.getProxyHost(), appSettings.getProxyPort()); + } + + private void resetProxy() { + appSettings.setProxyEnabled(false); + + //Temporary change thread policy + StrictMode.ThreadPolicy old = StrictMode.getThreadPolicy(); + StrictMode.ThreadPolicy tmp = new StrictMode.ThreadPolicy.Builder().permitAll().build(); + StrictMode.setThreadPolicy(tmp); + + NetCipher.clearProxy(); + try{ + WebkitProxy.resetProxy(MainActivity.class.getName(), this); + } catch (Exception e) { + //Nothing we can do. + } + + StrictMode.setThreadPolicy(old); + + //Restart app + Intent restartActivity = new Intent(this, MainActivity.class); + PendingIntent pendingIntent = PendingIntent.getActivity(this, 12374, restartActivity, PendingIntent.FLAG_CANCEL_CURRENT); + AlarmManager mgr = (AlarmManager) getSystemService(Context.ALARM_SERVICE); + mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, pendingIntent); + System.exit(0); + } } \ No newline at end of file diff --git a/app/src/main/java/com/github/dfa/diaspora_android/data/AppSettings.java b/app/src/main/java/com/github/dfa/diaspora_android/data/AppSettings.java index fe4e3178..77cc3ed7 100644 --- a/app/src/main/java/com/github/dfa/diaspora_android/data/AppSettings.java +++ b/app/src/main/java/com/github/dfa/diaspora_android/data/AppSettings.java @@ -70,6 +70,9 @@ public class AppSettings { private static final String PODUSERPROFILE_ID = "podUserProfile_guid"; private static final String PODDOMAIN = "podDomain"; private static final String PODUSERPROFILE_ASPECTS = "podUserProfile_aspects"; + private static final String PROXY_ENABLED = "isProxyEnabled"; + private static final String PROXY_HOST = "proxyHost"; + private static final String PROXY_PORT = "proxyPort"; } @@ -150,4 +153,41 @@ public class AppSettings { } return aspects; } + + public void setProxyEnabled(boolean enabled) { + //commit instead of apply because the app is likely to be killed before apply is called. + prefApp.edit().putBoolean(PREF.PROXY_ENABLED, enabled).commit(); + } + + /** + * Default return value: false + * @return whether proxy is enabled or not + */ + public boolean isProxyEnabled() { + return prefApp.getBoolean(PREF.PROXY_ENABLED, false); + } + + public void setProxyHost(String host) { + setString(prefApp, PREF.PROXY_HOST, host); + } + + /** + * Default value: "" + * @return proxy host + */ + public String getProxyHost() { + return prefApp.getString(PREF.PROXY_HOST, ""); + } + + public void setProxyPort(int port) { + setInt(prefApp, PREF.PROXY_PORT, port); + } + + /** + * Default value: 0 + * @return proxy port + */ + public int getProxyPort() { + return prefApp.getInt(PREF.PROXY_PORT, 0); + } } diff --git a/app/src/main/res/layout/proxy_configuration_dialog.xml b/app/src/main/res/layout/proxy_configuration_dialog.xml new file mode 100644 index 00000000..75bc4330 --- /dev/null +++ b/app/src/main/res/layout/proxy_configuration_dialog.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + \ 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 33c428cd..78f62f41 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -78,6 +78,10 @@ Do load images Do not load images Change view + Proxy + Enabled + Host + Port Share link as text Share screenshot of webpage Take screenshot of webpage @@ -218,4 +222,6 @@ https:// Share… #DiasporaForAndroid + + Warning: Could not set network proxy… From af0070df66faa9f9a1eedfef735b36c5f9bf41b5 Mon Sep 17 00:00:00 2001 From: vanitasvitae Date: Thu, 9 Jun 2016 01:53:47 +0200 Subject: [PATCH 2/3] Let elements other than the webView use the proxy (HttpsUrlConnections) --- .../diaspora_android/task/GetPodsService.java | 34 ++++++++++--------- .../task/ImageDownloadTask.java | 20 +++++++++-- .../task/ProfileFetchTask.java | 34 ++++++++++++++----- 3 files changed, 60 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/com/github/dfa/diaspora_android/task/GetPodsService.java b/app/src/main/java/com/github/dfa/diaspora_android/task/GetPodsService.java index 8791ac00..ba01716a 100644 --- a/app/src/main/java/com/github/dfa/diaspora_android/task/GetPodsService.java +++ b/app/src/main/java/com/github/dfa/diaspora_android/task/GetPodsService.java @@ -28,12 +28,6 @@ import android.util.Log; import com.github.dfa.diaspora_android.App; -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.StatusLine; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.DefaultHttpClient; import org.json.JSONArray; import org.json.JSONObject; @@ -44,6 +38,10 @@ import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; +import javax.net.ssl.HttpsURLConnection; + +import info.guardianproject.netcipher.NetCipher; + public class GetPodsService extends Service { public static final String MESSAGE_PODS_RECEIVED = "com.github.dfa.diaspora.podsreceived"; private static final String TAG = App.TAG; @@ -73,24 +71,28 @@ public class GetPodsService extends Service { // TODO: Update deprecated code StringBuilder builder = new StringBuilder(); - HttpClient client = new DefaultHttpClient(); + //HttpClient client = new DefaultHttpClient(); List list = null; + HttpsURLConnection connection; + InputStream inStream; try { - HttpGet httpGet = new HttpGet("http://podupti.me/api.php?key=4r45tg&format=json"); - HttpResponse response = client.execute(httpGet); - StatusLine statusLine = response.getStatusLine(); - int statusCode = statusLine.getStatusCode(); + connection = NetCipher.getHttpsURLConnection("https://podupti.me/api.php?key=4r45tg&format=json"); + int statusCode = connection.getResponseCode(); if (statusCode == 200) { - HttpEntity entity = response.getEntity(); - InputStream content = entity.getContent(); + inStream = connection.getInputStream(); BufferedReader reader = new BufferedReader( - new InputStreamReader(content)); + new InputStreamReader(inStream)); String line; while ((line = reader.readLine()) != null) { builder.append(line); } + + try { + inStream.close(); + } catch (IOException e) {/*Nothing to do*/} + + connection.disconnect(); } else { - //TODO Notify User about failure Log.e(TAG, "Failed to download list of pods"); } } catch (IOException e) { @@ -136,4 +138,4 @@ public class GetPodsService extends Service { throw new UnsupportedOperationException("Not yet implemented"); } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/github/dfa/diaspora_android/task/ImageDownloadTask.java b/app/src/main/java/com/github/dfa/diaspora_android/task/ImageDownloadTask.java index 2b5a3d23..d9059344 100644 --- a/app/src/main/java/com/github/dfa/diaspora_android/task/ImageDownloadTask.java +++ b/app/src/main/java/com/github/dfa/diaspora_android/task/ImageDownloadTask.java @@ -13,7 +13,12 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import javax.net.ssl.HttpsURLConnection; + +import info.guardianproject.netcipher.NetCipher; + /** + * Task that can be used to download images from URLs and store them in storage * Created by Gregor Santner (gsantner) on 24.03.16. */ public class ImageDownloadTask extends AsyncTask { @@ -35,9 +40,12 @@ public class ImageDownloadTask extends AsyncTask { String url = urls[0]; Bitmap bitmap = null; FileOutputStream out = null; + InputStream inStream; + HttpsURLConnection connection; try { - InputStream in = new java.net.URL(url).openStream(); - bitmap = BitmapFactory.decodeStream(in); + connection = NetCipher.getHttpsURLConnection(url); + inStream = connection.getInputStream(); + bitmap = BitmapFactory.decodeStream(inStream); // Save to file if not null if (savePath != null) { @@ -45,6 +53,12 @@ public class ImageDownloadTask extends AsyncTask { bitmap.compress(Bitmap.CompressFormat.PNG, 100, out); } + try { + inStream.close(); + } catch (IOException e) {/*Nothing*/} + + connection.disconnect(); + } catch (Exception e) { Log.e(App.TAG, e.getMessage()); } finally { @@ -64,4 +78,4 @@ public class ImageDownloadTask extends AsyncTask { imageView.setImageBitmap(result); } } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/github/dfa/diaspora_android/task/ProfileFetchTask.java b/app/src/main/java/com/github/dfa/diaspora_android/task/ProfileFetchTask.java index f54774f0..2771a4b7 100644 --- a/app/src/main/java/com/github/dfa/diaspora_android/task/ProfileFetchTask.java +++ b/app/src/main/java/com/github/dfa/diaspora_android/task/ProfileFetchTask.java @@ -10,11 +10,16 @@ import com.github.dfa.diaspora_android.data.PodUserProfile; import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; -import java.net.HttpURLConnection; import java.net.URL; +import javax.net.ssl.HttpsURLConnection; + +import info.guardianproject.netcipher.NetCipher; + /** + * AsyncTask to fetch a users profile * Created by Gregor Santner (gsantner) on 30.03.16. */ public class ProfileFetchTask extends AsyncTask { @@ -37,18 +42,21 @@ public class ProfileFetchTask extends AsyncTask { String cookies = cookieManager.getCookie("https://" + app.getSettings().getPodDomain()); Log.d(App.TAG, cookies); + HttpsURLConnection connection; + InputStream inStream; try { URL url = new URL("https://" + app.getSettings().getPodDomain() + "/stream"); - HttpURLConnection conn = (HttpURLConnection) url.openConnection(); - conn.setReadTimeout(10000); - conn.setConnectTimeout(15000); - conn.setRequestMethod("GET"); + connection = NetCipher.getHttpsURLConnection(url); + connection.setReadTimeout(10000); + connection.setConnectTimeout(15000); + connection.setRequestMethod("GET"); if (cookies != null) { - conn.setRequestProperty("Cookie", cookies); + connection.setRequestProperty("Cookie", cookies); } - conn.connect(); + connection.connect(); - BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream())); + inStream = connection.getInputStream(); + BufferedReader br = new BufferedReader(new InputStreamReader(inStream)); String line; final String TARGET_TAG = "window.gon={};gon.user="; while ((line = br.readLine()) != null && !line.startsWith(" { break; } } + + try{ + br.close(); + inStream.close(); + } catch (IOException e){/*Nothing*/} + + connection.disconnect(); + } catch (IOException e) { e.printStackTrace(); } @@ -70,4 +86,4 @@ public class ProfileFetchTask extends AsyncTask { return null; } -} +} \ No newline at end of file From b01a40bc076c922e530f48d518285bcd83f2264b Mon Sep 17 00:00:00 2001 From: vanitasvitae Date: Thu, 9 Jun 2016 01:57:50 +0200 Subject: [PATCH 3/3] Added german strings --- app/src/main/res/values-de/strings.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 20681ecb..daddb24e 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -171,5 +171,10 @@ along with this program. If not, see http://www.gnu.org/licenses.<br> <br Speichere Bild als Linkadresse kopiert … Teilen… + Aktiviert + Host + Proxy + Port + Warnung: Proxy konnte nicht aktiviert werden…