diff --git a/app/build.gradle b/app/build.gradle index 3072760b..a7f02870 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 4183f68e..708d5fd0 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 @@ -82,6 +82,7 @@ import com.github.dfa.diaspora_android.listener.WebUserProfileChangedListener; import com.github.dfa.diaspora_android.ui.ContextMenuWebView; import com.github.dfa.diaspora_android.ui.CustomWebViewClient; import com.github.dfa.diaspora_android.util.Helpers; +import com.github.dfa.diaspora_android.util.ProxyHandler; import org.json.JSONException; @@ -97,6 +98,7 @@ import java.util.Locale; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; +import info.guardianproject.netcipher.proxy.OrbotHelper; public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener, WebUserProfileChangedListener { @@ -174,6 +176,16 @@ public class MainActivity extends AppCompatActivity appSettings = app.getSettings(); podUserProfile = new PodUserProfile(app, uiHandler, this); + ProxyHandler.getInstance(this, appSettings).registerOrbotReceiver(this); + if(appSettings.isProxyOrbot()) { + if(!OrbotHelper.isOrbotInstalled(this)) { + appSettings.setProxyOrbot(false); + promptInstallOrbot(); + } else { + Toast.makeText(this, "Orbot Proxy: "+ProxyHandler.getInstance(null, null).getActiveProxy(), Toast.LENGTH_SHORT).show(); + } + } + this.registerForContextMenu(webView); webView.setParentActivity(this); webView.setOverScrollMode(WebView.OVER_SCROLL_ALWAYS); @@ -306,7 +318,13 @@ public class MainActivity extends AppCompatActivity } }); + if(!appSettings.isProxyOrbot()) { + ProxyHandler.getInstance(null, null).unregisterOrbotReceiver(this); + afterOnCreate(savedInstanceState); + } + } + private void afterOnCreate(Bundle savedInstanceState) { if (savedInstanceState == null) { if (Helpers.isOnline(MainActivity.this)) { webView.loadData("", "text/html", null); @@ -404,6 +422,9 @@ public class MainActivity extends AppCompatActivity @Override protected void onResume() { super.onResume(); + try { + ProxyHandler.getInstance(null, null).registerOrbotReceiver(this); + } catch (IllegalStateException e){} registerReceiver(brLoadUrl, new IntentFilter(URL_MESSAGE)); } @@ -441,6 +462,7 @@ public class MainActivity extends AppCompatActivity @Override protected void onPause() { unregisterReceiver(brLoadUrl); + ProxyHandler.getInstance(null, null).unregisterOrbotReceiver(this); super.onPause(); } @@ -926,7 +948,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), + appSettings.isProxyOrbot() ? getString(R.string.orbot_proxy_enabled) : getString(R.string.orbot_proxy_disabled)}; if (Helpers.isOnline(MainActivity.this)) { new AlertDialog.Builder(MainActivity.this) @@ -959,6 +982,13 @@ public class MainActivity extends AppCompatActivity }) .show(); break; + case 4: + if(appSettings.isProxyOrbot()) { + ProxyHandler.getInstance(null,null).setProxy(MainActivity.this, null, 0, ProxyHandler.NO_PROXY); + } else { + OrbotHelper.requestStartTor(MainActivity.this); + } + appSettings.setProxyOrbot(!appSettings.isProxyOrbot()); } } }).show(); @@ -1029,7 +1059,7 @@ public class MainActivity extends AppCompatActivity } DrawerLayout drawer = (DrawerLayout) findViewById(R.id.main__layout); - drawer.closeDrawer(GravityCompat.START); + if(drawer != null) drawer.closeDrawer(GravityCompat.START); return true; } @@ -1049,4 +1079,50 @@ public class MainActivity extends AppCompatActivity grantResults); } } + + /** + * Ask the user whether to install Orbot or not. Check if installing from + * F-Droid or Google Play, otherwise take the user to the Orbot download + * page on f-droid.org. + */ + void promptInstallOrbot() { + String message = this.getString(R.string.you_must_have_orbot) + " "; + + final Intent intent = OrbotHelper.getOrbotInstallIntent(MainActivity.this); + if (intent.getPackage() == null) { + message += MainActivity.this.getString(R.string.download_orbot_from_fdroid); + } else { + message += MainActivity.this.getString(R.string.get_orbot_from_fdroid); + } + + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.install_orbot_); + builder.setMessage(message); + builder.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + MainActivity.this.startActivity(intent); + } + }); + builder.setNegativeButton(android.R.string.no, null); + builder.show(); + } + + public void requestOrbotStart(boolean backgroundStartsDisabled) { + AlertDialog.Builder startDialog = new AlertDialog.Builder(this); + startDialog.setTitle(R.string.start_orbot_); + if(backgroundStartsDisabled) + startDialog.setMessage(R.string.orbot_starts_disabled_message); + else + startDialog.setMessage(R.string.orbot_doesn_t_appear_to_be_running_would_you_like_to_start_it_up_and_connect_to_tor_); + + startDialog.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + startActivityForResult(OrbotHelper.getShowOrbotStartIntent(), 1); + } + }); + startDialog.setNegativeButton(android.R.string.no, null); + startDialog.show(); + } } \ 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 12b0dd57..5e668386 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 @@ -65,6 +65,7 @@ public class AppSettings { private static final String PODUSERPROFILE_NAME = "podUserProfile_name"; private static final String PODUSERPROFILE_ID = "podUserProfile_guid"; private static final String PODDOMAIN = "podDomain"; + private static final String IS_PROXY_ORBOT = "proxyViaOrbot"; } @@ -132,4 +133,12 @@ public class AppSettings { public void setPreviousPodlist(String[] pods){ setStringArray(prefApp, PREF.PREVIOUS_PODLIST, pods); } + + public boolean isProxyOrbot() { + return prefApp.getBoolean(PREF.IS_PROXY_ORBOT, false); + } + + public void setProxyOrbot(boolean active) { + setBool(prefApp, PREF.IS_PROXY_ORBOT, active); + } } diff --git a/app/src/main/java/com/github/dfa/diaspora_android/util/ProxyHandler.java b/app/src/main/java/com/github/dfa/diaspora_android/util/ProxyHandler.java new file mode 100644 index 00000000..7cbb325d --- /dev/null +++ b/app/src/main/java/com/github/dfa/diaspora_android/util/ProxyHandler.java @@ -0,0 +1,143 @@ +package com.github.dfa.diaspora_android.util; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.util.Log; + +import com.github.dfa.diaspora_android.App; +import com.github.dfa.diaspora_android.activity.MainActivity; +import com.github.dfa.diaspora_android.data.AppSettings; + +import info.guardianproject.netcipher.NetCipher; +import info.guardianproject.netcipher.proxy.OrbotHelper; +import info.guardianproject.netcipher.web.WebkitProxy; + +/** + * Handle proxy configurations + * In this particular case integration of Orbot as a proxy for the tor network has been done, + * but other proxies can easily and similarly be added as well. + * Created by vanitas on 05.06.16. + */ +public class ProxyHandler { + //TODO: Remove when NetCipher > 1.2.1 releases + public final static String EXTRA_PROXY_PORT_HTTP = "org.torproject.android.intent.extra.HTTP_PROXY_PORT"; + //Proxy types + public static final int NO_PROXY = 0, ORBOT_PROXY = 1; + private int activeProxy = NO_PROXY; + + private static ProxyHandler instance; + private MainActivity mainActivity; + private AppSettings appSettings; + private OrbotReceiver orbotReceiver; + + + private ProxyHandler(MainActivity main, AppSettings settings) { + orbotReceiver = new OrbotReceiver(); + mainActivity = main; + appSettings = settings; + } + + public static ProxyHandler getInstance(MainActivity main, AppSettings settings) { + if(instance == null) instance = new ProxyHandler(main, settings); + return instance; + } + + public void registerOrbotReceiver(Context context) { + if(!orbotReceiver.isRegistered()) { + context.registerReceiver(orbotReceiver, new IntentFilter(OrbotHelper.ACTION_STATUS)); + orbotReceiver.setRegistered(true); + } + else throw new IllegalStateException("OrbotReceiver is already registered."); + } + + public void unregisterOrbotReceiver(Context context) { + if(orbotReceiver.isRegistered()) { + context.unregisterReceiver(orbotReceiver); + orbotReceiver.setRegistered(false); + } + else throw new IllegalStateException("OrbotReceiver was not registered and can therefore not be unregistered."); + } + + public void setProxy(Context context, String host, int port, int proxyType) { + if(proxyType == NO_PROXY) { + try { + NetCipher.clearProxy(); + WebkitProxy.resetProxy(MainActivity.class.getName(), context); + activeProxy = proxyType; + } + catch (Exception e) { + Log.e(App.TAG, "ProxyHandler caught exception "+e.getClass().getName()+" while resetting proxy."); + } + restartApplication(context); + return; + } + + if(proxyType == ORBOT_PROXY) { + try { + NetCipher.setProxy(host, port); + WebkitProxy.setProxy(MainActivity.class.getName(), context.getApplicationContext(), null, host, port); + activeProxy = proxyType; + } catch (Exception e) { + Log.d(App.TAG, "ProxyHandler caught exception " + e.getClass().getName() + " while setting proxy."); + e.printStackTrace(); + } + } + + //Add further proxies here + } + + private static void restartApplication(Context context) { + Intent mStartActivity = new Intent(context, MainActivity.class); + PendingIntent mPendingIntent = PendingIntent.getActivity(context, 12374, mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT); + AlarmManager mgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent); + System.exit(0); + } + + private static class OrbotReceiver extends BroadcastReceiver { + private boolean registered; + + private boolean orbotRunning = false; + private int proxyPort = -1; + private String proxyHost = "127.0.0.1"; + + + @Override + public void onReceive(Context context, Intent intent) { + if(OrbotHelper.ACTION_STATUS.equals(intent.getAction())) { + + if(OrbotHelper.STATUS_ON.equals(intent.getStringExtra(OrbotHelper.EXTRA_STATUS))) { + proxyPort = intent.getIntExtra(EXTRA_PROXY_PORT_HTTP, -1); + if(instance.appSettings.isProxyOrbot()) { + instance.setProxy(context, proxyHost, proxyPort, ORBOT_PROXY); + } + orbotRunning = true; + } + + if(OrbotHelper.STATUS_OFF.equals(intent.getStringExtra(OrbotHelper.EXTRA_STATUS))) { + instance.mainActivity.requestOrbotStart(false); + } + + if(OrbotHelper.STATUS_STARTS_DISABLED.equals(intent.getStringExtra(OrbotHelper.EXTRA_STATUS))) { + instance.mainActivity.requestOrbotStart(true); + } + } + } + + public boolean isRegistered() { + return registered; + } + + public void setRegistered(boolean r) { + registered = r; + } + } + + public int getActiveProxy() { + return activeProxy; + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ac6920ca..2fca7364 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -211,4 +211,15 @@ https:// Share… #DiasporaForAndroid + + Install Orbot? + You must have Orbot installed and activated to proxy traffic through it. + Would you like to install it from F-Droid? + Would you like to download it from f-droid.org? + Start Orbot? + Orbot doesn\'t appear to be running. Would you like to start it up and connect to Tor? + Orbot has background starts disabled. Would you like to open Orbot? + Orbot Proxy: Enabled + Orbot Proxy: Disabled +