Wait for Orbot to connect when proxying though Orbot before trying to load a URL.
[PrivacyBrowser.git] / app / src / main / java / com / stoutner / privacybrowser / activities / MainWebView.java
index cf7cbdad3fb7f817f4e9d172b9699fabcee0b71e..eb6392b2ad97d8fda4f0aac8ccbfeb8567cede16 100644 (file)
@@ -24,10 +24,12 @@ package com.stoutner.privacybrowser.activities;
 import android.annotation.SuppressLint;
 import android.app.DialogFragment;
 import android.app.DownloadManager;
+import android.content.BroadcastReceiver;
 import android.content.ClipData;
 import android.content.ClipboardManager;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.SharedPreferences;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
@@ -57,7 +59,6 @@ import android.support.v7.app.AppCompatDialogFragment;
 import android.support.v7.widget.Toolbar;
 import android.text.Editable;
 import android.text.TextWatcher;
-import android.util.Log;
 import android.util.Patterns;
 import android.view.ContextMenu;
 import android.view.GestureDetector;
@@ -129,6 +130,9 @@ public class MainWebView extends AppCompatActivity implements NavigationView.OnN
     // `sslCertificate` is public static so it can be accessed from `ViewSslCertificate`.  It is also used in `onCreate()`.
     public static SslCertificate sslCertificate;
 
+    // `orbotStatus` is public static so it can be accessed from `OrbotProxyHelper`.  It is also used in `onCreate()`.
+    public static String orbotStatus;
+
 
     // `drawerLayout` is used in `onCreate()`, `onNewIntent()`, and `onBackPressed()`.
     private DrawerLayout drawerLayout;
@@ -194,6 +198,15 @@ public class MainWebView extends AppCompatActivity implements NavigationView.OnN
     // `translucentNavigationBarOnFullscreen` is used in `onCreate()` and `applySettings()`.
     private boolean translucentNavigationBarOnFullscreen;
 
+    // `proxyThroughOrbot` is used in `onCreate()` and `applySettings()`
+    private boolean proxyThroughOrbot;
+
+    // `pendingUrl` is used in `onCreate()` and `applySettings()`
+    private static String pendingUrl;
+
+    // `waitingForOrbotData` is used in `onCreate()` and `applySettings()`.
+    private String waitingForOrbotHTMLString;
+
     // `findOnPageLinearLayout` is used in `onCreate()`, `onOptionsItemSelected()`, and `closeFindOnPage()`.
     private LinearLayout findOnPageLinearLayout;
 
@@ -268,6 +281,45 @@ public class MainWebView extends AppCompatActivity implements NavigationView.OnN
             }
         });
 
+        // Set `waitingForOrbotHTMLString`.
+        waitingForOrbotHTMLString = "<html><body><br/><center><h1>" + getString(R.string.waiting_for_orbot) + "</h1></center></body></html>";
+
+        // Initialize `pendingUrl`.
+        pendingUrl = "";
+
+        // Set the initial Orbot status.
+        orbotStatus = "unknown";
+
+        // Create an Orbot status `BroadcastReceiver`.
+        BroadcastReceiver orbotStatusBroadcastReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                // Store the content of the status message in `orbotStatus`.
+                orbotStatus = intent.getStringExtra("org.torproject.android.intent.extra.STATUS");
+
+                // If we are waiting on `pendingUrl`, load it now that Orbot is connected.
+                if (orbotStatus.equals("ON") && !pendingUrl.isEmpty()) {
+
+                    // Wait 500 milliseconds, because Orbot isn't really ready yet.
+                    try {
+                        Thread.sleep(500);
+                    } catch (InterruptedException exception) {
+                        // Do nothing.
+                    }
+
+                    // Copy `pendingUrl` to `formattedUrlString` and reset `pendingUrl` to be empty.
+                    formattedUrlString = pendingUrl;
+                    pendingUrl = "";
+
+                    // Load `formattedUrlString
+                    mainWebView.loadUrl(formattedUrlString, customHeaders);
+                }
+            }
+        };
+
+        // Register `orbotStatusBroadcastReceiver` on `this` context.
+        this.registerReceiver(orbotStatusBroadcastReceiver, new IntentFilter("org.torproject.android.intent.action.STATUS"));
+
         // Get handles for views that need to be accessed.
         drawerLayout = (DrawerLayout) findViewById(R.id.drawerlayout);
         rootCoordinatorLayout = (CoordinatorLayout) findViewById(R.id.root_coordinatorlayout);
@@ -559,25 +611,31 @@ public class MainWebView extends AppCompatActivity implements NavigationView.OnN
             // Update the URL in urlTextBox when the page starts to load.
             @Override
             public void onPageStarted(WebView view, String url, Bitmap favicon) {
-                // We need to update `formattedUrlString` at the beginning of the load, so that if the user toggles JavaScript during the load the new website is reloaded.
-                formattedUrlString = url;
+                // Check to see if we are waiting on Orbot.
+                if (pendingUrl.isEmpty()) {  // We are not waiting on Orbot, so we need to process the URL.
+                    // We need to update `formattedUrlString` at the beginning of the load, so that if the user toggles JavaScript during the load the new website is reloaded.
+                    formattedUrlString = url;
 
-                // Display the loading URL is the URL text box.
-                urlTextBox.setText(url);
+                    // Display the loading URL is the URL text box.
+                    urlTextBox.setText(url);
+                }
             }
 
             // Update formattedUrlString and urlTextBox.  It is necessary to do this after the page finishes loading because the final URL can change during load.
             @Override
             public void onPageFinished(WebView view, String url) {
-                formattedUrlString = url;
+                // Check to see if we are waiting on Orbot.
+                if (pendingUrl.isEmpty()) {  // we are not waiting on Orbot, so we need to process the URL.
+                    formattedUrlString = url;
 
-                // Only update urlTextBox if the user is not typing in it.
-                if (!urlTextBox.hasFocus()) {
-                    urlTextBox.setText(formattedUrlString);
-                }
+                    // Only update urlTextBox if the user is not typing in it.
+                    if (!urlTextBox.hasFocus()) {
+                        urlTextBox.setText(formattedUrlString);
+                    }
 
-                // Store the SSL certificate so it can be accessed from `ViewSslCertificate`.
-                sslCertificate = mainWebView.getCertificate();
+                    // Store the SSL certificate so it can be accessed from `ViewSslCertificate`.
+                    sslCertificate = mainWebView.getCertificate();
+                }
             }
 
             // Handle SSL Certificate errors.
@@ -698,9 +756,6 @@ public class MainWebView extends AppCompatActivity implements NavigationView.OnN
         // Initialize the default preference values the first time the program is run.  `this` is the context.  `false` keeps this command from resetting any current preferences back to default.
         PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
 
-        // Apply the settings from the shared preferences.
-        applySettings();
-
         // Get the intent information that started the app.
         final Intent intent = getIntent();
 
@@ -710,13 +765,13 @@ public class MainWebView extends AppCompatActivity implements NavigationView.OnN
             formattedUrlString = intentUriData.toString();
         }
 
-        // If formattedUrlString is null assign the homepage to it.
-        if (formattedUrlString == null) {
-            formattedUrlString = homepage;
-        }
+        // Apply the settings from the shared preferences.
+        applySettings();
 
-        // Load the initial website.
-        mainWebView.loadUrl(formattedUrlString, customHeaders);
+        // Load `formattedUrlString` if we are not proxying through Orbot and waiting for Orbot to connect.
+        if (!(proxyThroughOrbot & !orbotStatus.equals("ON"))) {
+            mainWebView.loadUrl(formattedUrlString, customHeaders);
+        }
 
         // If the favorite icon is null, load the default.
         if (favoriteIcon == null) {
@@ -1181,6 +1236,14 @@ public class MainWebView extends AppCompatActivity implements NavigationView.OnN
                 startActivity(settingsIntent);
                 break;
 
+            /*
+            case R.id.domains:
+                // Launch `DomainsList`.
+                Intent domainsIntent = new Intent(this, DomainsList.class);
+                startActivity(domainsIntent);
+                break;
+            */
+
             case R.id.guide:
                 // Launch `Guide`.
                 Intent guideIntent = new Intent(this, Guide.class);
@@ -1751,15 +1814,20 @@ public class MainWebView extends AppCompatActivity implements NavigationView.OnN
         String userAgentString = sharedPreferences.getString("user_agent", "PrivacyBrowser/1.0");
         String customUserAgentString = sharedPreferences.getString("custom_user_agent", "PrivacyBrowser/1.0");
         String javaScriptDisabledSearchString = sharedPreferences.getString("javascript_disabled_search", "https://duckduckgo.com/html/?q=");
-        String javaScriptDisabledCustomSearchString = sharedPreferences.getString("javascript_disabled_search_custom_url", "");
+        String javaScriptDisabledSearchCustomURLString = sharedPreferences.getString("javascript_disabled_search_custom_url", "");
         String javaScriptEnabledSearchString = sharedPreferences.getString("javascript_enabled_search", "https://duckduckgo.com/?q=");
-        String javaScriptEnabledCustomSearchString = sharedPreferences.getString("javascript_enabled_search_custom_url", "");
+        String javaScriptEnabledSearchCustomURLString = sharedPreferences.getString("javascript_enabled_search_custom_url", "");
         String homepageString = sharedPreferences.getString("homepage", "https://www.duckduckgo.com");
+        String torHomepageString = sharedPreferences.getString("tor_homepage", "https://3g2upl4pq6kufc4m.onion");
+        String torJavaScriptDisabledSearchString = sharedPreferences.getString("tor_javascript_disabled_search", "https://3g2upl4pq6kufc4m.onion/html/?q=");
+        String torJavaScriptDisabledSearchCustomURLString = sharedPreferences.getString("tor_javascript_disabled_search_custom_url", "");
+        String torJavaScriptEnabledSearchString = sharedPreferences.getString("tor_javascript_enabled_search", "https://3g2upl4pq6kufc4m.onion/?q=");
+        String torJavaScriptEnabledSearchCustomURLString = sharedPreferences.getString("tor_javascript_enabled_search_custom_url", "");
         String defaultFontSizeString = sharedPreferences.getString("default_font_size", "100");
         swipeToRefreshEnabled = sharedPreferences.getBoolean("swipe_to_refresh_enabled", false);
         adBlockerEnabled = sharedPreferences.getBoolean("block_ads", true);
         boolean doNotTrackEnabled = sharedPreferences.getBoolean("do_not_track", false);
-        boolean proxyThroughOrbot = sharedPreferences.getBoolean("proxy_through_orbot", false);
+        proxyThroughOrbot = sharedPreferences.getBoolean("proxy_through_orbot", false);
         fullScreenBrowsingModeEnabled = sharedPreferences.getBoolean("enable_full_screen_browsing_mode", false);
         hideSystemBarsOnFullscreen = sharedPreferences.getBoolean("hide_system_bars", false);
         translucentNavigationBarOnFullscreen = sharedPreferences.getBoolean("translucent_navigation_bar", true);
@@ -1786,8 +1854,75 @@ public class MainWebView extends AppCompatActivity implements NavigationView.OnN
             }
         }
 
-        // Apply the other settings from `sharedPreferences`.
-        homepage = homepageString;
+        // Set the homepage, search, and proxy options.
+        if (proxyThroughOrbot) {  // Set the Tor options.
+            // Set `torHomepageString` as `homepage`.
+            homepage = torHomepageString;
+
+            // If formattedUrlString is null assign the homepage to it.
+            if (formattedUrlString == null) {
+                formattedUrlString = homepage;
+            }
+
+            // Set JavaScript disabled search.
+            if (torJavaScriptDisabledSearchString.equals("Custom URL")) {  // Get the custom URL string.
+                javaScriptDisabledSearchURL = torJavaScriptDisabledSearchCustomURLString;
+            } else {  // Use the string from the pre-built list.
+                javaScriptDisabledSearchURL = torJavaScriptDisabledSearchString;
+            }
+
+            // Set JavaScript enabled search.
+            if (torJavaScriptEnabledSearchString.equals("Custom URL")) {  // Get the custom URL string.
+                javaScriptEnabledSearchURL = torJavaScriptEnabledSearchCustomURLString;
+            } else {  // Use the string from the pre-built list.
+                javaScriptEnabledSearchURL = torJavaScriptEnabledSearchString;
+            }
+
+            // Set the proxy.  `this` refers to the current activity where an `AlertDialog` might be displayed.
+            OrbotProxyHelper.setProxy(getApplicationContext(), this, "localhost", "8118");
+
+            // Display a message to the user if we are waiting on Orbot.
+            if (!orbotStatus.equals("ON")) {
+                // Save `formattedUrlString` in `pendingUrl`.
+                pendingUrl = formattedUrlString;
+
+                // Load a waiting page.  `null` specifies no encoding, which defaults to ASCII.
+                mainWebView.loadData(waitingForOrbotHTMLString, "text/html", null);
+            }
+        } else {  // Set the non-Tor options.
+            // Set `homepageString` as `homepage`.
+            homepage = homepageString;
+
+            // If formattedUrlString is null assign the homepage to it.
+            if (formattedUrlString == null) {
+                formattedUrlString = homepage;
+            }
+
+            // Set JavaScript disabled search.
+            if (javaScriptDisabledSearchString.equals("Custom URL")) {  // Get the custom URL string.
+                javaScriptDisabledSearchURL = javaScriptDisabledSearchCustomURLString;
+            } else {  // Use the string from the pre-built list.
+                javaScriptDisabledSearchURL = javaScriptDisabledSearchString;
+            }
+
+            // Set JavaScript enabled search.
+            if (javaScriptEnabledSearchString.equals("Custom URL")) {  // Get the custom URL string.
+                javaScriptEnabledSearchURL = javaScriptEnabledSearchCustomURLString;
+            } else {  // Use the string from the pre-built list.
+                javaScriptEnabledSearchURL = javaScriptEnabledSearchString;
+            }
+
+            // Reset the proxy to default.  The host is `""` and the port is `"0"`.
+            OrbotProxyHelper.setProxy(getApplicationContext(), this, "", "0");
+
+            // Reset `pendingUrl` if we are currently waiting for Orbot to connect.
+            if (!pendingUrl.isEmpty()) {
+                formattedUrlString = pendingUrl;
+                pendingUrl = "";
+            }
+        }
+
+        // Set swipe to refresh.
         swipeRefreshLayout.setEnabled(swipeToRefreshEnabled);
 
         // Set the user agent initial status.
@@ -1808,20 +1943,6 @@ public class MainWebView extends AppCompatActivity implements NavigationView.OnN
                 break;
         }
 
-        // Set JavaScript disabled search.
-        if (javaScriptDisabledSearchString.equals("Custom URL")) {  // Get the custom URL string.
-            javaScriptDisabledSearchURL = javaScriptDisabledCustomSearchString;
-        } else {  // Use the string from the pre-built list.
-            javaScriptDisabledSearchURL = javaScriptDisabledSearchString;
-        }
-
-        // Set JavaScript enabled search.
-        if (javaScriptEnabledSearchString.equals("Custom URL")) {  // Get the custom URL string.
-            javaScriptEnabledSearchURL = javaScriptEnabledCustomSearchString;
-        } else {  // Use the string from the pre-built list.
-            javaScriptEnabledSearchURL = javaScriptEnabledSearchString;
-        }
-
         // Set Do Not Track status.
         if (doNotTrackEnabled) {
             customHeaders.put("DNT", "1");
@@ -1829,15 +1950,7 @@ public class MainWebView extends AppCompatActivity implements NavigationView.OnN
             customHeaders.remove("DNT");
         }
 
-        // Set Orbot proxy status.
-        if (proxyThroughOrbot) {
-            // Set the proxy.  `this` refers to the current activity where an `AlertDialog` might be displayed.
-            OrbotProxyHelper.setProxy(getApplicationContext(), this, "localhost", "8118");
-        } else {  // Reset the proxy to default.  The host is `""` and the port is `"0"`.
-            OrbotProxyHelper.setProxy(getApplicationContext(), this, "", "0");
-        }
-
-        // If we are in full screen mode update the `SYSTEM_UI` flags.
+        // Update the `SYSTEM_UI` flags if we are in full screen mode.
         if (inFullScreenBrowsingMode) {
             if (hideSystemBarsOnFullscreen) {  // Hide everything.
                 // Remove the translucent navigation setting if it is currently flagged.