]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blobdiff - app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java
Update the privacy policy. https://redmine.stoutner.com/issues/244
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / activities / MainWebViewActivity.java
index 8aad8721b596501867e0a30f4fa9c759fabfa3cd..9cb3a71a4ce61628206eef57e3b0e81a1b438adf 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2015-2017 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2015-2018 Soren Stoutner <soren@stoutner.com>.
  *
  * Download cookie code contributed 2017 Hendrik Knackstedt.  Copyright assigned to Soren Stoutner <soren@stoutner.com>.
  *
@@ -54,6 +54,10 @@ import android.support.design.widget.NavigationView;
 import android.support.design.widget.Snackbar;
 import android.support.v4.app.ActivityCompat;
 import android.support.v4.content.ContextCompat;
+// `ShortcutInfoCompat`, `ShortcutManagerCompat`, and `IconCompat` can be switched to the non-compat version once API >= 26.
+import android.support.v4.content.pm.ShortcutInfoCompat;
+import android.support.v4.content.pm.ShortcutManagerCompat;
+import android.support.v4.graphics.drawable.IconCompat;
 import android.support.v4.view.GravityCompat;
 import android.support.v4.widget.DrawerLayout;
 import android.support.v4.widget.SwipeRefreshLayout;
@@ -66,6 +70,7 @@ import android.text.Editable;
 import android.text.Spanned;
 import android.text.TextWatcher;
 import android.text.style.ForegroundColorSpan;
+import android.util.Log;
 import android.util.Patterns;
 import android.view.ContextMenu;
 import android.view.GestureDetector;
@@ -163,6 +168,9 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
     // `webViewTitle` is public static so it can be accessed from `CreateBookmarkDialog` and `CreateHomeScreenShortcutDialog`.  It is also used in `onCreate()`.
     public static String webViewTitle;
 
+    // `appliedUserAgentString` is public static so it can be accessed from `ViewSourceActivity`.  It is also used in `applyDomainSettings()`.
+    public static String appliedUserAgentString;
+
     // `displayWebpageImagesBoolean` is public static so it can be accessed from `DomainSettingsFragment`.  It is also used in `applyAppSettings()` and `applyDomainSettings()`.
     public static boolean displayWebpageImagesBoolean;
 
@@ -175,6 +183,9 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
     // `restartFromBookmarksActivity` is public static so it can be accessed from `BookmarksActivity`.  It is also used in `onRestart()`.
     public static boolean restartFromBookmarksActivity;
 
+    // `easyListVersion` is public static so it can be accessed from `AboutTabFragment`.  It is also used in `onCreate()`.
+    public static String easyListVersion;
+
     // `currentBookmarksFolder` is public static so it can be accessed from `BookmarksActivity`.  It is also used in `onCreate()`, `onBackPressed()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and
     // `loadBookmarksFolder()`.
     public static String currentBookmarksFolder;
@@ -323,13 +334,9 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
     // `urlTextBox` is used in `onCreate()`, `onOptionsItemSelected()`, `loadUrlFromTextBox()`, `loadUrl()`, and `highlightUrlText()`.
     private EditText urlTextBox;
 
-    // `redColorSpan` is used in `onCreate()` and `highlightUrlText()`.
+    // The color spans are used in `onCreate()` and `highlightUrlText()`.
     private ForegroundColorSpan redColorSpan;
-
-    // `initialGrayColorSpan` is sued in `onCreate()` and `highlightUrlText()`.
     private ForegroundColorSpan initialGrayColorSpan;
-
-    // `finalGrayColorSpam` is used in `onCreate()` and `highlightUrlText()`.
     private ForegroundColorSpan finalGrayColorSpan;
 
     // `adView` is used in `onCreate()` and `onConfigurationChanged()`.
@@ -393,6 +400,52 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
         // Run the default commands.
         super.onCreate(savedInstanceState);
 
+        // **DEBUG** Log the beginning of the loading of the ad blocker.
+        Log.i("AdBlocker", "Begin loading ad blocker");
+
+        // Initialize `adServerSet`.
+        final Set<String> adServersSet = new HashSet<>();
+
+        // Load the list of ad servers into memory.
+        try {
+            // Load `easylist.txt` into a `BufferedReader`.
+            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(getAssets().open("easylist.txt")));
+
+            // Create a string for storing each ad server.
+            String adBlockerEntry;
+
+            // Populate `adServersSet`.
+            while ((adBlockerEntry = bufferedReader.readLine()) != null) {
+                //noinspection StatementWithEmptyBody
+                if (adBlockerEntry.contains("##") || adBlockerEntry.contains("#?#") || adBlockerEntry.contains("#@#") || adBlockerEntry.startsWith("[")) {
+                    // Entries that contain `##`, `#?#`, and `#@#` are for hiding elements in the main page's HTML.  Entries that start with `[` describe the AdBlock compatibility level.
+
+                    // Do nothing.  Privacy Browser does not currently use these entries.
+
+                    // **DEBUG** Log the entries that are not added.
+                    // Log.i("AdBlocker", "Not added: " + adBlockerEntry);
+                } else if (adBlockerEntry.startsWith("!")){  //  Entries that begin with `!` are comments.
+                    if (adBlockerEntry.startsWith("! Version:")) {
+                        // Store the EasyList version number.
+                        easyListVersion = adBlockerEntry.substring(11);
+                    }
+
+                    // **DEBUG** Log the entries that are not added.
+                    // Log.i("AdBlocker", "Not added: " + adBlockerEntry);
+                } else {
+                    adServersSet.add(adBlockerEntry);
+                }
+            }
+
+            // Close `bufferedReader`.
+            bufferedReader.close();
+        } catch (IOException e) {
+            // The asset exists, so the `IOException` will never be thrown.
+        }
+
+        // **DEBUG** Log the finishing of the loading of the ad blocker.
+        Log.i("AdBlocker", "Finish loading ad blocker");
+
         // Set the content view.
         setContentView(R.layout.main_drawerlayout);
 
@@ -411,16 +464,16 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
         appBar.setCustomView(R.layout.url_app_bar);
         appBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
 
-        // Initialize the `ForegroundColorSpans` and `StyleSpan` for highlighting `urlTextBox`.  We have to use the deprecated `getColor()` until API >= 23.
+        // Initialize the foreground color spans for highlighting the URLs.  We have to use the deprecated `getColor()` until API >= 23.
         redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700));
         initialGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500));
         finalGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500));
 
         // Get a handle for `urlTextBox`.
-        urlTextBox = appBar.getCustomView().findViewById(R.id.url_edittext);
+        urlTextBox = findViewById(R.id.url_edittext);
 
         // Remove the formatting from `urlTextBar` when the user is editing the text.
-        urlTextBox.setOnFocusChangeListener((v, hasFocus) -> {
+        urlTextBox.setOnFocusChangeListener((View v, boolean hasFocus) -> {
             if (hasFocus) {  // The user is editing `urlTextBox`.
                 // Remove the highlighting.
                 urlTextBox.getText().removeSpan(redColorSpan);
@@ -432,8 +485,8 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
             }
         });
 
-        // Set the `Go` button on the keyboard to load the URL in `urlTextBox`.
-        urlTextBox.setOnKeyListener((v, keyCode, event) -> {
+        // Set the go button on the keyboard to load the URL in `urlTextBox`.
+        urlTextBox.setOnKeyListener((View v, int keyCode, KeyEvent event) -> {
             // If the event is a key-down event on the `enter` button, load the URL.
             if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
                 // Load the URL into the mainWebView and consume the event.
@@ -790,28 +843,6 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
         // drawerToggle creates the hamburger icon at the start of the AppBar.
         drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, supportAppBar, R.string.open_navigation_drawer, R.string.close_navigation_drawer);
 
-        // Initialize `adServerSet`.
-        final Set<String> adServersSet = new HashSet<>();
-
-        // Load the list of ad servers into memory.
-        try {
-            // Load `pgl.yoyo.org_adservers.txt` into a `BufferedReader`.
-            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(getAssets().open("pgl.yoyo.org_adservers.txt")));
-
-            // Create a string for storing each ad server.
-            String adServer;
-
-            // Populate `adServersSet`.
-            while ((adServer = bufferedReader.readLine()) != null) {
-                adServersSet.add(adServer);
-            }
-
-            // Close `bufferedReader`.
-            bufferedReader.close();
-        } catch (IOException ioException) {
-            // We're pretty sure the asset exists, so we don't need to worry about the `IOException` ever being thrown.
-        }
-
         mainWebView.setWebViewClient(new WebViewClient() {
             // `shouldOverrideUrlLoading` makes this `WebView` the default handler for URLs inside the app, so that links are not kicked out to other apps.
             // We have to use the deprecated `shouldOverrideUrlLoading` until API >= 24.
@@ -1123,7 +1154,10 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
                             });
                 }
 
+                // Update the progress bar.
                 progressBar.setProgress(progress);
+
+                // Set the visibility of the progress bar.
                 if (progress < 100) {
                     // Show the progress bar.
                     progressBar.setVisibility(View.VISIBLE);
@@ -1217,7 +1251,7 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
         registerForContextMenu(mainWebView);
 
         // Allow the downloading of files.
-        mainWebView.setDownloadListener((url, userAgent, contentDisposition, mimetype, contentLength) -> {
+        mainWebView.setDownloadListener((String url, String userAgent, String contentDisposition, String mimetype, long contentLength) -> {
             // Show the `DownloadFileDialog` `AlertDialog` and name this instance `@string/download`.
             AppCompatDialogFragment downloadFileDialogFragment = DownloadFileDialog.fromUrl(url, contentDisposition, contentLength);
             downloadFileDialogFragment.show(getSupportFragmentManager(), getString(R.string.download));
@@ -1456,6 +1490,7 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
         MenuItem toggleThirdPartyCookiesMenuItem = menu.findItem(R.id.toggle_third_party_cookies);
         MenuItem toggleDomStorageMenuItem = menu.findItem(R.id.toggle_dom_storage);
         MenuItem toggleSaveFormDataMenuItem = menu.findItem(R.id.toggle_save_form_data);
+        MenuItem clearDataMenuItem = menu.findItem(R.id.clear_data);
         MenuItem clearCookiesMenuItem = menu.findItem(R.id.clear_cookies);
         MenuItem clearDOMStorageMenuItem = menu.findItem(R.id.clear_dom_storage);
         MenuItem clearFormDataMenuItem = menu.findItem(R.id.clear_form_data);
@@ -1507,6 +1542,9 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
         WebViewDatabase mainWebViewDatabase = WebViewDatabase.getInstance(this);
         clearFormDataMenuItem.setEnabled(mainWebViewDatabase.hasFormData());
 
+        // Enable `Clear Data` if any of the submenu items are enabled.
+        clearDataMenuItem.setEnabled(clearCookiesMenuItem.isEnabled() || clearDOMStorageMenuItem.isEnabled() || clearFormDataMenuItem.isEnabled());
+
         // Initialize font size variables.
         int fontSize = mainWebView.getSettings().getTextZoom();
         String fontSizeTitle;
@@ -1570,7 +1608,7 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
         // Run all the other default commands.
         super.onPrepareOptionsMenu(menu);
 
-        // `return true` displays the menu.
+        // Display the menu.
         return true;
     }
 
@@ -1580,6 +1618,7 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
     // removeAllCookies is deprecated, but it is required for API < 21.
     @SuppressWarnings("deprecation")
     public boolean onOptionsItemSelected(MenuItem menuItem) {
+        // Get the selected menu item ID.
         int menuItemId = menuItem.getItemId();
 
         // Set the commands that relate to the menu entries.
@@ -1774,9 +1813,16 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
                                         WebStorage webStorage = WebStorage.getInstance();
                                         webStorage.deleteAllData();
 
-                                        // Manually remove `IndexedDB` if it exists.
+                                        // Manually delete the DOM storage files and directories.
                                         try {
+                                            // A `String[]` must be used because the directory contains a space and `Runtime.exec` will otherwise not escape the string correctly.
+                                            privacyBrowserRuntime.exec(new String[] {"rm", "-rf", privateDataDirectoryString + "/app_webview/Local Storage/"});
+
+                                            // Multiple commands must be used because `Runtime.exec()` does not like `*`.
                                             privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/IndexedDB");
+                                            privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager");
+                                            privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager-journal");
+                                            privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/databases");
                                         } catch (IOException e) {
                                             // Do nothing if an error is thrown.
                                         }
@@ -1900,6 +1946,12 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
                 printManager.print(getString(R.string.privacy_browser_web_page), printDocumentAdapter, null);
                 return true;
 
+            case R.id.view_source:
+                // Launch the Vew Source activity.
+                Intent viewSourceIntent = new Intent(this, ViewSourceActivity.class);
+                startActivity(viewSourceIntent);
+                return true;
+
             case R.id.add_to_homescreen:
                 // Show the `CreateHomeScreenShortcutDialog` `AlertDialog` and name this instance `R.string.create_shortcut`.
                 AppCompatDialogFragment createHomeScreenShortcutDialogFragment = new CreateHomeScreenShortcutDialog();
@@ -2033,10 +2085,10 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
 
                     // Manually delete the DOM storage files and directories, as `WebStorage` sometimes will not flush its changes to disk before `System.exit(0)` is run.
                     try {
-                        // We have to use a `String[]` because the directory contains a space and `Runtime.exec` will not escape the string correctly otherwise.
+                        // A `String[]` must be used because the directory contains a space and `Runtime.exec` will otherwise not escape the string correctly.
                         privacyBrowserRuntime.exec(new String[] {"rm", "-rf", privateDataDirectoryString + "/app_webview/Local Storage/"});
 
-                        // We have to use multiple commands because `Runtime.exec()` does not like `*`.
+                        // Multiple commands must be used because `Runtime.exec()` does not like `*`.
                         privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/IndexedDB");
                         privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager");
                         privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager-journal");
@@ -2244,7 +2296,7 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
                 });
 
                 // Add a `Download Image` entry.
-                menu.add(R.string.download_image).setOnMenuItemClickListener(item -> {
+                menu.add(R.string.download_image).setOnMenuItemClickListener((MenuItem item) -> {
                     // Show the `DownloadImageDialog` `AlertDialog` and name this instance `@string/download`.
                     AppCompatDialogFragment downloadImageDialogFragment = DownloadImageDialog.imageUrl(imageUrl);
                     downloadImageDialogFragment.show(getSupportFragmentManager(), getString(R.string.download));
@@ -2281,7 +2333,7 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
                 });
 
                 // Add a `Download Image` entry.
-                menu.add(R.string.download_image).setOnMenuItemClickListener(item -> {
+                menu.add(R.string.download_image).setOnMenuItemClickListener((MenuItem item) -> {
                     // Show the `DownloadImageDialog` `AlertDialog` and name this instance `@string/download`.
                     AppCompatDialogFragment downloadImageDialogFragment = DownloadImageDialog.imageUrl(imageUrl);
                     downloadImageDialogFragment.show(getSupportFragmentManager(), getString(R.string.download));
@@ -2409,21 +2461,28 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
 
     @Override
     public void onCreateHomeScreenShortcut(AppCompatDialogFragment dialogFragment) {
-        // Get shortcutNameEditText from the alert dialog.
+        // Get the shortcut name.
         EditText shortcutNameEditText = dialogFragment.getDialog().findViewById(R.id.shortcut_name_edittext);
+        String shortcutNameString = shortcutNameEditText.getText().toString();
+
+        // Convert the favorite icon bitmap to an `Icon`.  `IconCompat` is required until API >= 26.
+        IconCompat favoriteIcon = IconCompat.createWithBitmap(favoriteIconBitmap);
 
-        // Create the bookmark shortcut based on formattedUrlString.
-        Intent bookmarkShortcut = new Intent();
-        bookmarkShortcut.setAction(Intent.ACTION_VIEW);
-        bookmarkShortcut.setData(Uri.parse(formattedUrlString));
-
-        // Place the bookmark shortcut on the home screen.
-        Intent placeBookmarkShortcut = new Intent();
-        placeBookmarkShortcut.putExtra("android.intent.extra.shortcut.INTENT", bookmarkShortcut);
-        placeBookmarkShortcut.putExtra("android.intent.extra.shortcut.NAME", shortcutNameEditText.getText().toString());
-        placeBookmarkShortcut.putExtra("android.intent.extra.shortcut.ICON", favoriteIconBitmap);
-        placeBookmarkShortcut.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
-        sendBroadcast(placeBookmarkShortcut);
+        // Setup the shortcut intent.
+        Intent shortcutIntent = new Intent();
+        shortcutIntent.setAction(Intent.ACTION_VIEW);
+        shortcutIntent.setData(Uri.parse(formattedUrlString));
+
+        // Create a shortcut info builder.  The shortcut name becomes the shortcut ID.
+        ShortcutInfoCompat.Builder shortcutInfoBuilder = new ShortcutInfoCompat.Builder(this, shortcutNameString);
+
+        // Add the required fields to the shortcut info builder.
+        shortcutInfoBuilder.setIcon(favoriteIcon);
+        shortcutInfoBuilder.setIntent(shortcutIntent);
+        shortcutInfoBuilder.setShortLabel(shortcutNameString);
+
+        // Request the pin.  `ShortcutManagerCompat` can be switched to `ShortcutManager` once API >= 26.
+        ShortcutManagerCompat.requestPinShortcut(this, shortcutInfoBuilder.build(), null);
     }
 
     @Override
@@ -2802,10 +2861,10 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
     }
 
     private void applyAppSettings() {
-        // Get a handle for `sharedPreferences`.  `this` references the current context.
+        // Get a handle for the shared preferences.
         SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
 
-        // Store the values from `sharedPreferences` in variables.
+        // Store the values from the shared preferences in variables.
         String homepageString = sharedPreferences.getString("homepage", "https://start.duckduckgo.com");
         String torHomepageString = sharedPreferences.getString("tor_homepage", "https://3g2upl4pq6kufc4m.onion");
         String torSearchString = sharedPreferences.getString("tor_search", "https://3g2upl4pq6kufc4m.onion/html/?q=");
@@ -3157,6 +3216,9 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
                             // Use the selected user agent.
                             mainWebView.getSettings().setUserAgentString(userAgentString);
                     }
+
+                    // Store the applied user agent string.
+                    appliedUserAgentString = mainWebView.getSettings().getUserAgentString();
                 }
 
                 // Set a green background on `urlTextBox` to indicate that custom domain settings are being used.  We have to use the deprecated `.getDrawable()` until the minimum API >= 21.
@@ -3219,6 +3281,9 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
                             // Use the selected user agent.
                             mainWebView.getSettings().setUserAgentString(defaultUserAgentString);
                     }
+
+                    // Store the applied user agent string.
+                    appliedUserAgentString = mainWebView.getSettings().getUserAgentString();
                 }
 
                 // Set a transparent background on `urlTextBox`.  We have to use the deprecated `.getDrawable()` until the minimum API >= 21.
@@ -3325,9 +3390,9 @@ public class MainWebViewActivity extends AppCompatActivity implements AddDomainD
     private void highlightUrlText() {
         String urlString = urlTextBox.getText().toString();
 
-        if (urlString.startsWith("http://")) {  // Highlight connections that are not encrypted.
+        if (urlString.startsWith("http://")) {  // Highlight the protocol of connections that are not encrypted.
             urlTextBox.getText().setSpan(redColorSpan, 0, 7, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
-        } else if (urlString.startsWith("https://")) {  // Highlight connections that are encrypted.
+        } else if (urlString.startsWith("https://")) {  // De-emphasize the protocol of connections that are encrypted.
             urlTextBox.getText().setSpan(initialGrayColorSpan, 0, 8, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
         }