Make SSL errors tab aware.
authorSoren Stoutner <soren@stoutner.com>
Thu, 18 Apr 2019 19:44:53 +0000 (12:44 -0700)
committerSoren Stoutner <soren@stoutner.com>
Thu, 18 Apr 2019 19:44:53 +0000 (12:44 -0700)
22 files changed:
.idea/codeStyles/Project.xml
app/src/free/java/com/stoutner/privacybrowser/dialogs/AdConsentDialog.java
app/src/main/java/com/stoutner/privacybrowser/activities/AboutActivity.java
app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksActivity.java
app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java
app/src/main/java/com/stoutner/privacybrowser/adapters/AboutPagerAdapter.java [new file with mode: 0644]
app/src/main/java/com/stoutner/privacybrowser/dialogs/AboutViewSourceDialog.java
app/src/main/java/com/stoutner/privacybrowser/dialogs/HttpAuthenticationDialog.java
app/src/main/java/com/stoutner/privacybrowser/dialogs/PinnedMismatchDialog.java
app/src/main/java/com/stoutner/privacybrowser/dialogs/SslCertificateErrorDialog.java
app/src/main/java/com/stoutner/privacybrowser/fragments/AboutTabFragment.java
app/src/main/java/com/stoutner/privacybrowser/views/NestedScrollWebView.java
app/src/main/res/layout/main_framelayout.xml
app/src/main/res/layout/webview_framelayout.xml
app/src/main/res/values-de/strings.xml
app/src/main/res/values-it/strings.xml
app/src/main/res/values-tr/strings.xml
app/src/main/res/values-v21/styles.xml
app/src/main/res/values/attrs.xml
app/src/main/res/values/styles.xml
build.gradle
gradle/wrapper/gradle-wrapper.properties

index 92d6b7d..86a569b 100644 (file)
         </value>
       </option>
     </JavaCodeStyleSettings>
-    <Objective-C-extensions>
-      <file>
-        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
-        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
-        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
-        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
-        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
-        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
-        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
-        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
-        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
-      </file>
-      <class>
-        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
-        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
-        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
-        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
-        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
-        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
-      </class>
-      <extensions>
-        <pair source="cpp" header="h" fileNamingConvention="NONE" />
-        <pair source="c" header="h" fileNamingConvention="NONE" />
-      </extensions>
-    </Objective-C-extensions>
     <XML>
       <option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
     </XML>
index c0cf972..bce865f 100644 (file)
@@ -22,14 +22,16 @@ package com.stoutner.privacybrowser.dialogs;
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.content.DialogInterface;
+import android.content.SharedPreferences;
 import android.os.Build;
 import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.view.WindowManager;
 
 import androidx.annotation.NonNull;
 import androidx.fragment.app.DialogFragment;
 
 import com.stoutner.privacybrowser.R;
-import com.stoutner.privacybrowser.activities.MainWebViewActivity;
 import com.stoutner.privacybrowser.helpers.AdConsentDatabaseHelper;
 import com.stoutner.privacybrowser.helpers.AdHelper;
 
@@ -37,11 +39,18 @@ public class AdConsentDialog extends DialogFragment {
     @NonNull
     @Override
     public Dialog onCreateDialog(Bundle savedInstanceState) {
+        // Get a handle for the shared preferences.
+        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());
+
+        // Get the screenshot and theme preferences.
+        boolean darkTheme = sharedPreferences.getBoolean("dark_theme", false);
+        boolean allowScreenshots = sharedPreferences.getBoolean("allow_screenshots", false);
+
         // Use a builder to create the alert dialog.
         AlertDialog.Builder dialogBuilder;
 
         // Set the style and the icon according to the theme.
-        if (MainWebViewActivity.darkTheme) {
+        if (darkTheme) {
             dialogBuilder = new AlertDialog.Builder(getActivity(), R.style.PrivacyBrowserAlertDialogDark);
             dialogBuilder.setIcon(R.drawable.block_ads_enabled_dark);
         } else {
@@ -87,8 +96,20 @@ public class AdConsentDialog extends DialogFragment {
             AdHelper.loadAd(getActivity().findViewById(R.id.adview), getActivity().getApplicationContext(), getString(R.string.ad_unit_id));
         });
 
+        // Create an alert dialog from the alert dialog builder.
+        AlertDialog alertDialog = dialogBuilder.create();
+
+        // Disable screenshots if not allowed.
+        if (!allowScreenshots) {
+            // Remove the warning below that `getWindow()` might be null.
+            assert alertDialog.getWindow() != null;
+
+            // Disable screenshots.
+            alertDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
+        }
+
         // Return the alert dialog.
-        return dialogBuilder.create();
+        return alertDialog;
     }
 
     // Close Privacy Browser Free if the dialog is cancelled without selecting a button (by tapping on the background).
index e15bfe7..6ce5b43 100644 (file)
@@ -19,6 +19,7 @@
 
 package com.stoutner.privacybrowser.activities;
 
+import android.content.Intent;
 import android.content.SharedPreferences;
 import android.os.Bundle;
 import android.preference.PreferenceManager;
@@ -27,14 +28,11 @@ import android.view.WindowManager;
 import androidx.appcompat.app.ActionBar;
 import androidx.appcompat.app.AppCompatActivity;
 import androidx.appcompat.widget.Toolbar;
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentManager;
-import androidx.fragment.app.FragmentPagerAdapter;
 import androidx.viewpager.widget.ViewPager;
 
 import com.google.android.material.tabs.TabLayout;
 
-import com.stoutner.privacybrowser.fragments.AboutTabFragment;
+import com.stoutner.privacybrowser.adapters.AboutPagerAdapter;
 import com.stoutner.privacybrowser.R;
 
 public class AboutActivity extends AppCompatActivity {
@@ -62,6 +60,12 @@ public class AboutActivity extends AppCompatActivity {
         // Run the default commands.
         super.onCreate(savedInstanceState);
 
+        // Get the intent that launched the activity.
+        Intent launchingIntent = getIntent();
+
+        // Store the blocklist versions.
+        String[] blocklistVersions = launchingIntent.getStringArrayExtra("blocklist_versions");
+
         // Set the content view.
         setContentView(R.layout.about_coordinatorlayout);
 
@@ -83,58 +87,9 @@ public class AboutActivity extends AppCompatActivity {
         actionBar.setDisplayHomeAsUpEnabled(true);
 
         //  Setup the ViewPager.
-        aboutViewPager.setAdapter(new AboutPagerAdapter(getSupportFragmentManager()));
+        aboutViewPager.setAdapter(new AboutPagerAdapter(getSupportFragmentManager(), getApplicationContext(), blocklistVersions));
 
         // Connect the tab layout to the view pager.
         aboutTabLayout.setupWithViewPager(aboutViewPager);
     }
-
-    private class AboutPagerAdapter extends FragmentPagerAdapter {
-        private AboutPagerAdapter(FragmentManager fragmentManager) {
-            // Run the default commands.
-            super(fragmentManager);
-        }
-
-        @Override
-        // Get the count of the number of tabs.
-        public int getCount() {
-            return 7;
-        }
-
-        @Override
-        // Get the name of each tab.  Tab numbers start at 0.
-        public CharSequence getPageTitle(int tab) {
-            switch (tab) {
-                case 0:
-                    return getString(R.string.version);
-
-                case 1:
-                    return getString(R.string.permissions);
-
-                case 2:
-                    return getString(R.string.privacy_policy);
-
-                case 3:
-                    return getString(R.string.changelog);
-
-                case 4:
-                    return getString(R.string.licenses);
-
-                case 5:
-                    return getString(R.string.contributors);
-
-                case 6:
-                    return getString(R.string.links);
-
-                default:
-                    return "";
-            }
-        }
-
-        @Override
-        // Setup each tab.
-        public Fragment getItem(int tab) {
-            return AboutTabFragment.createTab(tab);
-        }
-    }
-}
+}
\ No newline at end of file
index d29e54c..12a96ae 100644 (file)
@@ -478,50 +478,45 @@ public class BookmarksActivity extends AppCompatActivity implements CreateBookma
                                     @SuppressLint("SwitchIntDef")  // Ignore the lint warning about not handling the other possible events as they are covered by `default:`.
                                     @Override
                                     public void onDismissed(Snackbar snackbar, int event) {
-                                        switch (event) {
-                                            // The user pushed the `Undo` button.
-                                            case Snackbar.Callback.DISMISS_EVENT_ACTION:
-                                                // Update the bookmarks cursor with the current contents of the bookmarks database, including the "deleted" bookmarks.
-                                                bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolder);
-
-                                                // Update the list view.
-                                                bookmarksCursorAdapter.changeCursor(bookmarksCursor);
-
-                                                // Re-select the previously selected bookmarks.
-                                                for (int i = 0; i < selectedBookmarksPositionsSparseBooleanArray.size(); i++) {
-                                                    bookmarksListView.setItemChecked(selectedBookmarksPositionsSparseBooleanArray.keyAt(i), true);
-                                                }
-                                                break;
-
-                                            // The Snackbar was dismissed without the `Undo` button being pushed.
-                                            default:
-                                                // Delete each selected bookmark.
-                                                for (long databaseIdLong : selectedBookmarksIdsLongArray) {
-                                                    // Convert `databaseIdLong` to an int.
-                                                    int databaseIdInt = (int) databaseIdLong;
-
-                                                    // Delete the contents of the folder if the selected bookmark is a folder.
-                                                    if (bookmarksDatabaseHelper.isFolder(databaseIdInt)) {
-                                                        deleteBookmarkFolderContents(databaseIdInt);
-                                                    }
-
-                                                    // Delete the selected bookmark.
-                                                    bookmarksDatabaseHelper.deleteBookmark(databaseIdInt);
+                                        if (event == Snackbar.Callback.DISMISS_EVENT_ACTION) {  // The user pushed the undo button.
+                                            // Update the bookmarks cursor with the current contents of the bookmarks database, including the "deleted" bookmarks.
+                                            bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolder);
+
+                                            // Update the list view.
+                                            bookmarksCursorAdapter.changeCursor(bookmarksCursor);
+
+                                            // Re-select the previously selected bookmarks.
+                                            for (int i = 0; i < selectedBookmarksPositionsSparseBooleanArray.size(); i++) {
+                                                bookmarksListView.setItemChecked(selectedBookmarksPositionsSparseBooleanArray.keyAt(i), true);
+                                            }
+                                        } else {  // The snackbar was dismissed without the undo button being pushed.
+                                            // Delete each selected bookmark.
+                                            for (long databaseIdLong : selectedBookmarksIdsLongArray) {
+                                                // Convert `databaseIdLong` to an int.
+                                                int databaseIdInt = (int) databaseIdLong;
+
+                                                // Delete the contents of the folder if the selected bookmark is a folder.
+                                                if (bookmarksDatabaseHelper.isFolder(databaseIdInt)) {
+                                                    deleteBookmarkFolderContents(databaseIdInt);
                                                 }
 
-                                                // Update the display order.
-                                                for (int i = 0; i < bookmarksListView.getCount(); i++) {
-                                                    // Get the database ID for the current bookmark.
-                                                    int currentBookmarkDatabaseId = (int) bookmarksListView.getItemIdAtPosition(i);
+                                                // Delete the selected bookmark.
+                                                bookmarksDatabaseHelper.deleteBookmark(databaseIdInt);
+                                            }
+
+                                            // Update the display order.
+                                            for (int i = 0; i < bookmarksListView.getCount(); i++) {
+                                                // Get the database ID for the current bookmark.
+                                                int currentBookmarkDatabaseId = (int) bookmarksListView.getItemIdAtPosition(i);
 
-                                                    // Move `bookmarksCursor` to the current bookmark position.
-                                                    bookmarksCursor.moveToPosition(i);
+                                                // Move `bookmarksCursor` to the current bookmark position.
+                                                bookmarksCursor.moveToPosition(i);
 
-                                                    // Update the display order only if it is not correct in the database.
-                                                    if (bookmarksCursor.getInt(bookmarksCursor.getColumnIndex(BookmarksDatabaseHelper.DISPLAY_ORDER)) != i) {
-                                                        bookmarksDatabaseHelper.updateDisplayOrder(currentBookmarkDatabaseId, i);
-                                                    }
+                                                // Update the display order only if it is not correct in the database.
+                                                if (bookmarksCursor.getInt(bookmarksCursor.getColumnIndex(BookmarksDatabaseHelper.DISPLAY_ORDER)) != i) {
+                                                    bookmarksDatabaseHelper.updateDisplayOrder(currentBookmarkDatabaseId, i);
                                                 }
+                                            }
                                         }
 
                                         // Reset the deleting bookmarks flag.
index 2be2341..4050a01 100644 (file)
@@ -157,17 +157,19 @@ import java.util.Set;
 // TODO.  Store up reloads for tabs that are not visible.
 // TODO.  New tabs are white in dark mode.
 // TODO.  Hide the tabs in full screen mode.
+// TODO.  Find on page.
+// TODO.  Use TabLayout.setScrollPosition to scroll to new tabs.
 
 // AppCompatActivity from android.support.v7.app.AppCompatActivity must be used to have access to the SupportActionBar until the minimum API is >= 21.
 public class MainWebViewActivity extends AppCompatActivity implements CreateBookmarkDialog.CreateBookmarkListener, CreateBookmarkFolderDialog.CreateBookmarkFolderListener,
         DownloadFileDialog.DownloadFileListener, DownloadImageDialog.DownloadImageListener, DownloadLocationPermissionDialog.DownloadLocationPermissionDialogListener, EditBookmarkDialog.EditBookmarkListener,
-        EditBookmarkFolderDialog.EditBookmarkFolderListener, HttpAuthenticationDialog.HttpAuthenticationListener, NavigationView.OnNavigationItemSelectedListener, WebViewTabFragment.NewTabListener,
-        PinnedMismatchDialog.PinnedMismatchListener, SslCertificateErrorDialog.SslCertificateErrorListener, UrlHistoryDialog.UrlHistoryListener {
+        EditBookmarkFolderDialog.EditBookmarkFolderListener, NavigationView.OnNavigationItemSelectedListener, WebViewTabFragment.NewTabListener, PinnedMismatchDialog.PinnedMismatchListener,
+        UrlHistoryDialog.UrlHistoryListener {
 
     // `orbotStatus` is public static so it can be accessed from `OrbotProxyHelper`.  It is also used in `onCreate()`, `onResume()`, and `applyProxyThroughOrbot()`.
     public static String orbotStatus;
 
-    // The WebView pager adapter is accessed from `PinnedMismatchDialog`.  It is also used in `onCreate()`, `onResume()`, and `addTab()`.
+    // The WebView pager adapter is accessed from `HttpAuthenticationDialog`, `PinnedMismatchDialog`, and `SslCertificateErrorDialog`.  It is also used in `onCreate()`, `onResume()`, and `addTab()`.
     public static WebViewPagerAdapter webViewPagerAdapter;
 
     // `reloadOnRestart` is public static so it can be accessed from `SettingsFragment`.  It is used in `onRestart()`
@@ -180,13 +182,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     // `restartFromBookmarksActivity` is public static so it can be accessed from `BookmarksActivity`.  It is also used in `onRestart()`.
     public static boolean restartFromBookmarksActivity;
 
-    // The blocklist versions are public static so they can be accessed from `AboutTabFragment`.  They are also used in `onCreate()`.  // TODO.
-    public static String easyListVersion;
-    public static String easyPrivacyVersion;
-    public static String fanboysAnnoyanceVersion;
-    public static String fanboysSocialVersion;
-    public static String ultraPrivacyVersion;
-
     // `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;
@@ -201,9 +196,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
 
 
-    // `navigatingHistory` is used in `onCreate()`, `onNavigationItemSelected()`, `onSslMismatchBack()`, and `applyDomainSettings()`.
-    private boolean navigatingHistory;  // TODO.
-
     // The current WebView is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onRestart()`, `onCreateContextMenu()`, `findPreviousOnPage()`,
     // `findNextOnPage()`, `closeFindOnPage()`, `loadUrlFromTextBox()`, `onSslMismatchBack()`, `applyProxyThroughOrbot()`, and `applyDomainSettings()`.
     private NestedScrollWebView currentWebView;
@@ -217,43 +209,30 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     // The options menu is set in `onCreateOptionsMenu()` and used in `onOptionsItemSelected()`, `updatePrivacyIcons()`, and `initializeWebView()`.
     private Menu optionsMenu;
 
-    // TODO.  This could probably be removed.
-    // The blocklist helper is used in `onCreate()` and `WebViewPagerAdapter`.
-    private BlockListHelper blockListHelper;
-
-    // The blocklists are populated in `onCreate()` and accessed from `WebViewPagerAdapter`.
+    // The blocklists are populated in `onCreate()` and accessed from `initializeWebView()`.
     private ArrayList<List<String[]>> easyList;
     private ArrayList<List<String[]>> easyPrivacy;
     private ArrayList<List<String[]>> fanboysAnnoyanceList;
     private ArrayList<List<String[]>> fanboysSocialList;
     private ArrayList<List<String[]>> ultraPrivacy;
 
-    // The blocklist menu items are used in `onCreateOptionsMenu()`, `onPrepareOptionsMenu()`, and `initializeWebView()`.  // TODO.
-    private MenuItem blocklistsMenuItem;
-    private MenuItem easyListMenuItem;
-    private MenuItem easyPrivacyMenuItem;
-    private MenuItem fanboysAnnoyanceListMenuItem;
-    private MenuItem fanboysSocialBlockingListMenuItem;
-    private MenuItem ultraPrivacyMenuItem;
-    private MenuItem blockAllThirdPartyRequestsMenuItem;
-
     // `webViewDefaultUserAgent` is used in `onCreate()` and `onPrepareOptionsMenu()`.
     private String webViewDefaultUserAgent;
 
     // `proxyThroughOrbot` is used in `onRestart()`, `onOptionsItemSelected()`, `applyAppSettings()`, and `applyProxyThroughOrbot()`.
     private boolean proxyThroughOrbot;
 
-    // `incognitoModeEnabled` is used in `onCreate()` and `applyAppSettings()`.
-    private boolean incognitoModeEnabled;  // TODO.
+    // The incognito mode is set in `applyAppSettings()` and used in `initializeWebView()`.
+    private boolean incognitoModeEnabled;
 
-    // `fullScreenBrowsingModeEnabled` is used in `onCreate()` and `applyAppSettings()`.
-    private boolean fullScreenBrowsingModeEnabled;  // TODO.
+    // The full screen browsing mode tracker is set it `applyAppSettings()` and used in `initializeWebView()`.
+    private boolean fullScreenBrowsingModeEnabled;
 
     // `inFullScreenBrowsingMode` is used in `onCreate()`, `onConfigurationChanged()`, and `applyAppSettings()`.
     private boolean inFullScreenBrowsingMode;
 
-    // Hide app bar is used in `onCreate()` and `applyAppSettings()`.
-    private boolean hideAppBar;  // TODO.
+    // Hide app bar is used in `applyAppSettings()` and `initializeWebView()`.
+    private boolean hideAppBar;
 
     // `reapplyDomainSettingsOnRestart` is used in `onCreate()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onRestart()`, and `onAddDomain()`, .
     private boolean reapplyDomainSettingsOnRestart;
@@ -264,24 +243,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     // `displayingFullScreenVideo` is used in `onCreate()` and `onResume()`.
     private boolean displayingFullScreenVideo;
 
-    // `downloadWithExternalApp` is used in `onCreate()`, `onCreateContextMenu()`, and `applyDomainSettings()`.
-    private boolean downloadWithExternalApp;  // TODO.
-
     // `orbotStatusBroadcastReceiver` is used in `onCreate()` and `onDestroy()`.
     private BroadcastReceiver orbotStatusBroadcastReceiver;
 
     // `waitingForOrbot` is used in `onCreate()`, `onResume()`, and `applyProxyThroughOrbot()`.
     private boolean waitingForOrbot;
 
-    // `domainSettingsJavaScriptEnabled` is used in `onOptionsItemSelected()` and `applyDomainSettings()`.
-    private Boolean domainSettingsJavaScriptEnabled;  // TODO.
-
-    // `waitingForOrbotHtmlString` is used in `onCreate()` and `applyProxyThroughOrbot()`.
-    private String waitingForOrbotHtmlString;  // TODO.
-
-    // `privateDataDirectoryString` is used in `onCreate()`, `onOptionsItemSelected()`, and `onNavigationItemSelected()`.
-    private String privateDataDirectoryString;  // TODO.
-
     // The action bar drawer toggle is initialized in `onCreate()` and used in `onResume()`.
     private ActionBarDrawerToggle actionBarDrawerToggle;
 
@@ -295,12 +262,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     private int drawerHeaderPaddingTop;
     private int drawerHeaderPaddingBottom;
 
-    // `sslErrorHandler` is used in `onCreate()`, `onSslErrorCancel()`, and `onSslErrorProceed`.
-    private SslErrorHandler sslErrorHandler;  // TODO.
-
-    // `httpAuthHandler` is used in `onCreate()`, `onHttpAuthenticationCancel()`, and `onHttpAuthenticationProceed()`.
-    private static HttpAuthHandler httpAuthHandler;  // TODO.
-
     // `bookmarksDatabaseHelper` is used in `onCreate()`, `onDestroy`, `onOptionsItemSelected()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`,
     // and `loadBookmarksFolder()`.
     private BookmarksDatabaseHelper bookmarksDatabaseHelper;
@@ -419,9 +380,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             }
         });
 
-        // Set `waitingForOrbotHTMLString`.
-        waitingForOrbotHtmlString = "<html><body><br/><center><h1>" + getString(R.string.waiting_for_orbot) + "</h1></center></body></html>";
-
         // Initialize the Orbot status and the waiting for Orbot trackers.
         orbotStatus = "unknown";
         waitingForOrbot = false;
@@ -479,8 +437,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Register `orbotStatusBroadcastReceiver` on `this` context.
         this.registerReceiver(orbotStatusBroadcastReceiver, new IntentFilter("org.torproject.android.intent.action.STATUS"));
 
-        // Instantiate the block list helper.
-        blockListHelper = new BlockListHelper();
+        // Instantiate the blocklist helper.
+        BlockListHelper blockListHelper = new BlockListHelper();
 
         // Parse the block lists.
         easyList = blockListHelper.parseBlockList(getAssets(), "blocklists/easylist.txt");
@@ -489,13 +447,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         fanboysSocialList = blockListHelper.parseBlockList(getAssets(), "blocklists/fanboy-social.txt");
         ultraPrivacy = blockListHelper.parseBlockList(getAssets(), "blocklists/ultraprivacy.txt");
 
-        // Store the list versions.
-        easyListVersion = easyList.get(0).get(0)[0];
-        easyPrivacyVersion = easyPrivacy.get(0).get(0)[0];
-        fanboysAnnoyanceVersion = fanboysAnnoyanceList.get(0).get(0)[0];
-        fanboysSocialVersion = fanboysSocialList.get(0).get(0)[0];
-        ultraPrivacyVersion = ultraPrivacy.get(0).get(0)[0];
-
         // Get handles for views that need to be modified.
         DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
         NavigationView navigationView = findViewById(R.id.navigationview);
@@ -824,13 +775,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Initialize the default preference values the first time the program is run.  `false` keeps this command from resetting any current preferences back to default.
         PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
 
-        // Store the application's private data directory.
-        privateDataDirectoryString = getApplicationInfo().dataDir;
-        // `dataDir` will vary, but will be something like `/data/user/0/com.stoutner.privacybrowser.standard`, which links to `/data/data/com.stoutner.privacybrowser.standard`.
-
-        // Initialize `inFullScreenBrowsingMode`, which is always false at this point because Privacy Browser never starts in full screen browsing mode.
-        inFullScreenBrowsingMode = false;
-
         // Inflate a bare WebView to get the default user agent.  It is not used to render content on the screen.
         @SuppressLint("InflateParams") View webViewLayout = getLayoutInflater().inflate(R.layout.bare_webview, null, false);
 
@@ -1024,7 +968,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             currentWebView.getSettings().setUseWideViewPort(false);
 
             // Load a waiting page.  `null` specifies no encoding, which defaults to ASCII.
-            currentWebView.loadData(waitingForOrbotHtmlString, "text/html", null);
+            currentWebView.loadData("<html><body><br/><center><h1>" + getString(R.string.waiting_for_orbot) + "</h1></center></body></html>", "text/html", null);
         }
 
         if (displayingFullScreenVideo || inFullScreenBrowsingMode) {
@@ -1111,13 +1055,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         MenuItem toggleSaveFormDataMenuItem = menu.findItem(R.id.toggle_save_form_data);  // Form data can be removed once the minimum API >= 26.
         MenuItem clearFormDataMenuItem = menu.findItem(R.id.clear_form_data);  // Form data can be removed once the minimum API >= 26.
         MenuItem refreshMenuItem = menu.findItem(R.id.refresh);
-        blocklistsMenuItem = menu.findItem(R.id.blocklists);
-        easyListMenuItem = menu.findItem(R.id.easylist);
-        easyPrivacyMenuItem = menu.findItem(R.id.easyprivacy);
-        fanboysAnnoyanceListMenuItem = menu.findItem(R.id.fanboys_annoyance_list);
-        fanboysSocialBlockingListMenuItem = menu.findItem(R.id.fanboys_social_blocking_list);
-        ultraPrivacyMenuItem = menu.findItem(R.id.ultraprivacy);
-        blockAllThirdPartyRequestsMenuItem = menu.findItem(R.id.block_all_third_party_requests);
         MenuItem adConsentMenuItem = menu.findItem(R.id.ad_consent);
 
         // Only display third-party cookies if API >= 21
@@ -1181,6 +1118,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         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);  // Form data can be removed once the minimum API >= 26.
+        MenuItem blocklistsMenuItem = menu.findItem(R.id.blocklists);
+        MenuItem easyListMenuItem = menu.findItem(R.id.easylist);
+        MenuItem easyPrivacyMenuItem = menu.findItem(R.id.easyprivacy);
+        MenuItem fanboysAnnoyanceListMenuItem = menu.findItem(R.id.fanboys_annoyance_list);
+        MenuItem fanboysSocialBlockingListMenuItem = menu.findItem(R.id.fanboys_social_blocking_list);
+        MenuItem ultraPrivacyMenuItem = menu.findItem(R.id.ultraprivacy);
+        MenuItem blockAllThirdPartyRequestsMenuItem = menu.findItem(R.id.block_all_third_party_requests);
         MenuItem fontSizeMenuItem = menu.findItem(R.id.font_size);
         MenuItem swipeToRefreshMenuItem = menu.findItem(R.id.swipe_to_refresh);
         MenuItem displayImagesMenuItem = menu.findItem(R.id.display_images);
@@ -1251,6 +1195,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Enable Clear Cookies if there are any.
         clearCookiesMenuItem.setEnabled(cookieManager.hasCookies());
 
+        // Get the application's private data directory, which will be something like `/data/user/0/com.stoutner.privacybrowser.standard`, which links to `/data/data/com.stoutner.privacybrowser.standard`.
+        String privateDataDirectoryString = getApplicationInfo().dataDir;
+
         // Get a count of the number of files in the Local Storage directory.
         File localStorageDirectory = new File (privateDataDirectoryString + "/app_webview/Local Storage/");
         int localStorageDirectoryNumberOfFiles = 0;
@@ -1577,20 +1524,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                             @SuppressLint("SwitchIntDef")  // Ignore the lint warning about not handling the other possible events as they are covered by `default:`.
                             @Override
                             public void onDismissed(Snackbar snackbar, int event) {
-                                switch (event) {
-                                    // The user pushed the undo button.
-                                    case Snackbar.Callback.DISMISS_EVENT_ACTION:
-                                        // Do nothing.
-                                        break;
-
-                                    // The snackbar was dismissed without the undo button being pushed.
-                                    default:
-                                        // `cookieManager.removeAllCookie()` varies by SDK.
-                                        if (Build.VERSION.SDK_INT < 21) {
-                                            cookieManager.removeAllCookie();
-                                        } else {
-                                            cookieManager.removeAllCookies(null);
-                                        }
+                                if (event != Snackbar.Callback.DISMISS_EVENT_ACTION) {  // The snackbar was dismissed without the undo button being pushed.
+                                    // Delete the cookies, which command varies by SDK.
+                                    if (Build.VERSION.SDK_INT < 21) {
+                                        cookieManager.removeAllCookie();
+                                    } else {
+                                        cookieManager.removeAllCookies(null);
+                                    }
                                 }
                             }
                         })
@@ -1606,49 +1546,46 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                             @SuppressLint("SwitchIntDef")  // Ignore the lint warning about not handling the other possible events as they are covered by `default:`.
                             @Override
                             public void onDismissed(Snackbar snackbar, int event) {
-                                switch (event) {
-                                    // The user pushed the undo button.
-                                    case Snackbar.Callback.DISMISS_EVENT_ACTION:
-                                        // Do nothing.
-                                        break;
-
-                                    // The snackbar was dismissed without the undo button being pushed.
-                                    default:
-                                        // Delete the DOM Storage.
-                                        WebStorage webStorage = WebStorage.getInstance();
-                                        webStorage.deleteAllData();
-
-                                        // Initialize a handler to manually delete the DOM storage files and directories.
-                                        Handler deleteDomStorageHandler = new Handler();
-
-                                        // Setup a runnable to manually delete the DOM storage files and directories.
-                                        Runnable deleteDomStorageRunnable = () -> {
-                                            try {
-                                                // Get a handle for the runtime.
-                                                Runtime runtime = Runtime.getRuntime();
-
-                                                // A string array must be used because the directory contains a space and `Runtime.exec` will otherwise not escape the string correctly.
-                                                Process deleteLocalStorageProcess = runtime.exec(new String[]{"rm", "-rf", privateDataDirectoryString + "/app_webview/Local Storage/"});
-
-                                                // Multiple commands must be used because `Runtime.exec()` does not like `*`.
-                                                Process deleteIndexProcess = runtime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/IndexedDB");
-                                                Process deleteQuotaManagerProcess = runtime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager");
-                                                Process deleteQuotaManagerJournalProcess = runtime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager-journal");
-                                                Process deleteDatabasesProcess = runtime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/databases");
-
-                                                // Wait for the processes to finish.
-                                                deleteLocalStorageProcess.waitFor();
-                                                deleteIndexProcess.waitFor();
-                                                deleteQuotaManagerProcess.waitFor();
-                                                deleteQuotaManagerJournalProcess.waitFor();
-                                                deleteDatabasesProcess.waitFor();
-                                            } catch (Exception exception) {
-                                                // Do nothing if an error is thrown.
-                                            }
-                                        };
-
-                                        // Manually delete the DOM storage files after 200 milliseconds.
-                                        deleteDomStorageHandler.postDelayed(deleteDomStorageRunnable, 200);
+                                if (event != Snackbar.Callback.DISMISS_EVENT_ACTION) {  // The snackbar was dismissed without the undo button being pushed.
+                                    // Delete the DOM Storage.
+                                    WebStorage webStorage = WebStorage.getInstance();
+                                    webStorage.deleteAllData();
+
+                                    // Initialize a handler to manually delete the DOM storage files and directories.
+                                    Handler deleteDomStorageHandler = new Handler();
+
+                                    // Setup a runnable to manually delete the DOM storage files and directories.
+                                    Runnable deleteDomStorageRunnable = () -> {
+                                        try {
+                                            // Get a handle for the runtime.
+                                            Runtime runtime = Runtime.getRuntime();
+
+                                            // Get the application's private data directory, which will be something like `/data/user/0/com.stoutner.privacybrowser.standard`,
+                                            // which links to `/data/data/com.stoutner.privacybrowser.standard`.
+                                            String privateDataDirectoryString = getApplicationInfo().dataDir;
+
+                                            // A string array must be used because the directory contains a space and `Runtime.exec` will otherwise not escape the string correctly.
+                                            Process deleteLocalStorageProcess = runtime.exec(new String[]{"rm", "-rf", privateDataDirectoryString + "/app_webview/Local Storage/"});
+
+                                            // Multiple commands must be used because `Runtime.exec()` does not like `*`.
+                                            Process deleteIndexProcess = runtime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/IndexedDB");
+                                            Process deleteQuotaManagerProcess = runtime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager");
+                                            Process deleteQuotaManagerJournalProcess = runtime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager-journal");
+                                            Process deleteDatabasesProcess = runtime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/databases");
+
+                                            // Wait for the processes to finish.
+                                            deleteLocalStorageProcess.waitFor();
+                                            deleteIndexProcess.waitFor();
+                                            deleteQuotaManagerProcess.waitFor();
+                                            deleteQuotaManagerJournalProcess.waitFor();
+                                            deleteDatabasesProcess.waitFor();
+                                        } catch (Exception exception) {
+                                            // Do nothing if an error is thrown.
+                                        }
+                                    };
+
+                                    // Manually delete the DOM storage files after 200 milliseconds.
+                                    deleteDomStorageHandler.postDelayed(deleteDomStorageRunnable, 200);
                                 }
                             }
                         })
@@ -1665,17 +1602,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                             @SuppressLint("SwitchIntDef")  // Ignore the lint warning about not handling the other possible events as they are covered by `default:`.
                             @Override
                             public void onDismissed(Snackbar snackbar, int event) {
-                                switch (event) {
-                                    // The user pushed the undo button.
-                                    case Snackbar.Callback.DISMISS_EVENT_ACTION:
-                                        // Do nothing.
-                                        break;
-
-                                    // The snackbar was dismissed without the `Undo` button being pushed.
-                                    default:
-                                        // Delete the form data.
-                                        WebViewDatabase mainWebViewDatabase = WebViewDatabase.getInstance(getApplicationContext());
-                                        mainWebViewDatabase.clearFormData();
+                                if (event != Snackbar.Callback.DISMISS_EVENT_ACTION) {  // The snackbar was dismissed without the undo button being pushed.
+                                    // Delete the form data.
+                                    WebViewDatabase mainWebViewDatabase = WebViewDatabase.getInstance(getApplicationContext());
+                                    mainWebViewDatabase.clearFormData();
                                 }
                             }
                         })
@@ -1933,7 +1863,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     currentWebView.getSettings().setJavaScriptEnabled(true);
                 } else if (currentWebView.getDomainSettingsApplied()) {  // Night mode is disabled and domain settings are applied.  Set JavaScript according to the domain settings.
                     // Apply the JavaScript preference that was stored the last time domain settings were loaded.
-                    currentWebView.getSettings().setJavaScriptEnabled(domainSettingsJavaScriptEnabled);  // TODO.
+                    currentWebView.getSettings().setJavaScriptEnabled(currentWebView.getDomainSettingsJavaScriptEnabled());
                 } else {  // Night mode is disabled and domain settings are not applied.  Set JavaScript according to the global preference.
                     // Apply the JavaScript preference.
                     currentWebView.getSettings().setJavaScriptEnabled(sharedPreferences.getBoolean("javascript", false));
@@ -2062,7 +1992,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     }
 
     // removeAllCookies is deprecated, but it is required for API < 21.
-    @SuppressWarnings("deprecation")
     @Override
     public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) {
         // Get the menu item ID.
@@ -2101,6 +2030,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Get a handle for the runtime.
                 Runtime runtime = Runtime.getRuntime();
 
+                // Get the application's private data directory, which will be something like `/data/user/0/com.stoutner.privacybrowser.standard`,
+                // which links to `/data/data/com.stoutner.privacybrowser.standard`.
+                String privateDataDirectoryString = getApplicationInfo().dataDir;
+
                 // Clear cookies.
                 if (clearEverything || sharedPreferences.getBoolean("clear_cookies", true)) {
                     // The command to remove cookies changed slightly in API 21.
@@ -2276,8 +2209,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     // Reset the current domain name so that navigation works if third-party requests are blocked.
                     currentWebView.resetCurrentDomainName();
 
-                    // Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded.
-                    navigatingHistory = true;
+                    // Set navigating history so that the domain settings are applied when the new URL is loaded.
+                    currentWebView.setNavigatingHistory(true);
 
                     // Load the previous website in the history.
                     currentWebView.goBack();
@@ -2289,8 +2222,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     // Reset the current domain name so that navigation works if third-party requests are blocked.
                     currentWebView.resetCurrentDomainName();
 
-                    // Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded.
-                    navigatingHistory = true;
+                    // Set navigating history so that the domain settings are applied when the new URL is loaded.
+                    currentWebView.setNavigatingHistory(true);
 
                     // Load the next website in the history.
                     currentWebView.goForward();
@@ -2380,8 +2313,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 break;
 
             case R.id.about:
-                // Launch `AboutActivity`.
+                // Create an intent to launch the about activity.
                 Intent aboutIntent = new Intent(this, AboutActivity.class);
+
+                // Create a string array for the blocklist versions.
+                String[] blocklistVersions = new String[] {easyList.get(0).get(0)[0], easyPrivacy.get(0).get(0)[0], fanboysAnnoyanceList.get(0).get(0)[0], fanboysSocialList.get(0).get(0)[0],
+                        ultraPrivacy.get(0).get(0)[0]};
+
+                // Add the blocklist versions to the intent.
+                aboutIntent.putExtra("blocklist_versions", blocklistVersions);
+
+                // Make it so.
                 startActivity(aboutIntent);
                 break;
         }
@@ -2440,9 +2382,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         final String imageUrl;
         final String linkUrl;
 
-        // Get handles for the the clipboard and fragment managers.
+        // Get handles for the system managers.
         final ClipboardManager clipboardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
         FragmentManager fragmentManager = getSupportFragmentManager();
+        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
 
         // Remove the lint errors below that the clipboard manager might be null.
         assert clipboardManager != null;
@@ -2492,7 +2435,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Add a Download URL entry.
                 menu.add(R.string.download_url).setOnMenuItemClickListener((MenuItem item) -> {
                     // Check if the download should be processed by an external app.
-                    if (downloadWithExternalApp) {  // Download with an external app.
+                    if (sharedPreferences.getBoolean("download_with_external_app", false)) {  // Download with an external app.
                         openUrlWithExternalApp(linkUrl);
                     } else {  // Download with Android's download manager.
                         // Check to see if the storage permission has already been granted.
@@ -2582,7 +2525,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Add a Download Image entry.
                 menu.add(R.string.download_image).setOnMenuItemClickListener((MenuItem item) -> {
                     // Check if the download should be processed by an external app.
-                    if (downloadWithExternalApp) {  // Download with an external app.
+                    if (sharedPreferences.getBoolean("download_with_external_app", false)) {  // Download with an external app.
                         openUrlWithExternalApp(imageUrl);
                     } else {  // Download with Android's download manager.
                         // Check to see if the storage permission has already been granted.
@@ -2656,7 +2599,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Add a `Download Image` entry.
                 menu.add(R.string.download_image).setOnMenuItemClickListener((MenuItem item) -> {
                     // Check if the download should be processed by an external app.
-                    if (downloadWithExternalApp) {  // Download with an external app.
+                    if (sharedPreferences.getBoolean("download_with_external_app", false)) {  // Download with an external app.
                         openUrlWithExternalApp(imageUrl);
                     } else {  // Download with Android's download manager.
                         // Check to see if the storage permission has already been granted.
@@ -3093,40 +3036,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         }
     }
 
-    @Override
-    public void onHttpAuthenticationCancel() {
-        // Cancel the `HttpAuthHandler`.
-        httpAuthHandler.cancel();
-    }
-
-    @Override
-    public void onHttpAuthenticationProceed(DialogFragment dialogFragment) {
-        // Get handles for the `EditTexts`.
-        EditText usernameEditText = dialogFragment.getDialog().findViewById(R.id.http_authentication_username);
-        EditText passwordEditText = dialogFragment.getDialog().findViewById(R.id.http_authentication_password);
-
-        // Proceed with the HTTP authentication.
-        httpAuthHandler.proceed(usernameEditText.getText().toString(), passwordEditText.getText().toString());
-    }
-
-    @Override
-    public void onSslErrorCancel() {  // TODO.  How to handle this with multiple tabs?  There could be multiple errors at once.
-        sslErrorHandler.cancel();
-    }
-
-    @Override
-    public void onSslErrorProceed() {  // TODO.  How to handle this with multiple tabs?  There could be multiple errors at once.
-        sslErrorHandler.proceed();
-    }
-
     @Override
     public void onPinnedMismatchBack() {  // TODO.  Move this logic to the dialog.
         if (currentWebView.canGoBack()) {  // There is a back page in the history.
             // Reset the current domain name so that navigation works if third-party requests are blocked.
             currentWebView.resetCurrentDomainName();
 
-            // Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded.
-            navigatingHistory = true;  // TODO.
+            // Set navigating history so that the domain settings are applied when the new URL is loaded.
+            currentWebView.setNavigatingHistory(true);
 
             // Go back.
             currentWebView.goBack();
@@ -3143,19 +3060,19 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     }
 
     @Override
-    public void onUrlHistoryEntrySelected(int moveBackOrForwardSteps) {
+    public void onUrlHistoryEntrySelected(int moveBackOrForwardSteps) {  // TODO.  Move this logic to the dialog.
         // Reset the current domain name so that navigation works if third-party requests are blocked.
         currentWebView.resetCurrentDomainName();
 
-        // Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded.
-        navigatingHistory = true;
+        // Set navigating history so that the domain settings are applied when the new URL is loaded.
+        currentWebView.setNavigatingHistory(true);
 
         // Load the history entry.
         currentWebView.goBackOrForward(moveBackOrForwardSteps);
     }
 
     @Override
-    public void onClearHistory() {
+    public void onClearHistory() {  // TODO.  Move this logic to the dialog.
         // Clear the history.
         currentWebView.clearHistory();
     }
@@ -3184,8 +3101,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             // Reset the current domain name so that navigation works if third-party requests are blocked.
             currentWebView.resetCurrentDomainName();
 
-            // Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded.
-            navigatingHistory = true;
+            // Set navigating history so that the domain settings are applied when the new URL is loaded.
+            currentWebView.setNavigatingHistory(true);
 
             // Go back.
             currentWebView.goBack();
@@ -3331,7 +3248,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         proxyThroughOrbot = sharedPreferences.getBoolean("proxy_through_orbot", false);
         fullScreenBrowsingModeEnabled = sharedPreferences.getBoolean("full_screen_browsing_mode", false);
         hideAppBar = sharedPreferences.getBoolean("hide_app_bar", true);
-        downloadWithExternalApp = sharedPreferences.getBoolean("download_with_external_app", false);
 
         // Get handles for the views that need to be modified.  `getSupportActionBar()` must be used until the minimum API >= 21.
         FrameLayout rootFrameLayout = findViewById(R.id.root_framelayout);
@@ -3553,7 +3469,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                 // Get the settings from the cursor.
                 nestedScrollWebView.setDomainSettingsDatabaseId(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper._ID)));
-                boolean domainJavaScriptEnabled = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_JAVASCRIPT)) == 1);
+                nestedScrollWebView.setDomainSettingsJavaScriptEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_JAVASCRIPT)) == 1);
                 nestedScrollWebView.setAcceptFirstPartyCookies(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FIRST_PARTY_COOKIES)) == 1);
                 boolean domainThirdPartyCookiesEnabled = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_THIRD_PARTY_COOKIES)) == 1);
                 nestedScrollWebView.getSettings().setDomStorageEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_DOM_STORAGE)) == 1);
@@ -3633,17 +3549,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                         break;
                 }
 
-                // TODO.
-                // Store the domain JavaScript status.  This is used by the options menu night mode toggle.
-                domainSettingsJavaScriptEnabled = domainJavaScriptEnabled;
-
                 // Enable JavaScript if night mode is enabled.
                 if (nestedScrollWebView.getNightMode()) {
                     // Enable JavaScript.
                     nestedScrollWebView.getSettings().setJavaScriptEnabled(true);
                 } else {
                     // Set JavaScript according to the domain settings.
-                    nestedScrollWebView.getSettings().setJavaScriptEnabled(domainJavaScriptEnabled);
+                    nestedScrollWebView.getSettings().setJavaScriptEnabled(nestedScrollWebView.getDomainSettingsJavaScriptEnabled());
                 }
 
                 // Close the current host domain settings cursor.
@@ -3910,7 +3822,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 currentWebView.getSettings().setUseWideViewPort(false);
 
                 // Load a waiting page.  `null` specifies no encoding, which defaults to ASCII.
-                currentWebView.loadData(waitingForOrbotHtmlString, "text/html", null);
+                currentWebView.loadData("<html><body><br/><center><h1>" + getString(R.string.waiting_for_orbot) + "</h1></center></body></html>", "text/html", null);
             } else if (reloadWebsite) {  // Orbot is ready and the website should be reloaded.
                 // Reload the website.
                 currentWebView.reload();
@@ -4233,63 +4145,63 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Get the fragment view.
         View fragmentView = webViewTabFragment.getView();
 
-        // Remove the incorrect lint warning below that the fragment view might be null.
-        assert fragmentView != null;
-
-        // Store the current WebView.
-        currentWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
+        // Set the current WebView if the fragment view is not null.
+        if (fragmentView != null) {
+            // Store the current WebView.
+            currentWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
 
-        // Update the status of swipe to refresh.
-        if (currentWebView.getSwipeToRefresh()) {  // Swipe to refresh is enabled.
-            if (Build.VERSION.SDK_INT >= 23) {  // For API >= 23, swipe refresh layout is continuously updated with an on scroll change listener and only enabled if the WebView is scrolled to the top.
-                // Enable the swipe refresh layout if the WebView is scrolled all the way to the top.
-                swipeRefreshLayout.setEnabled(currentWebView.getY() == 0);
-            } else {
-                // Enable the swipe refresh layout.
-                swipeRefreshLayout.setEnabled(true);
+            // Update the status of swipe to refresh.
+            if (currentWebView.getSwipeToRefresh()) {  // Swipe to refresh is enabled.
+                if (Build.VERSION.SDK_INT >= 23) {  // For API >= 23, swipe refresh layout is continuously updated with an on scroll change listener and only enabled if the WebView is scrolled to the top.
+                    // Enable the swipe refresh layout if the WebView is scrolled all the way to the top.
+                    swipeRefreshLayout.setEnabled(currentWebView.getY() == 0);
+                } else {
+                    // Enable the swipe refresh layout.
+                    swipeRefreshLayout.setEnabled(true);
+                }
+            } else {  // Swipe to refresh is disabled.
+                // Disable the swipe refresh layout.
+                swipeRefreshLayout.setEnabled(false);
             }
-        } else {  // Swipe to refresh is disabled.
-            // Disable the swipe refresh layout.
-            swipeRefreshLayout.setEnabled(false);
-        }
 
-        // Get a handle for the cookie manager.
-        CookieManager cookieManager = CookieManager.getInstance();
+            // Get a handle for the cookie manager.
+            CookieManager cookieManager = CookieManager.getInstance();
 
-        // Set the first-party cookie status.
-        cookieManager.setAcceptCookie(currentWebView.getAcceptFirstPartyCookies());
+            // Set the first-party cookie status.
+            cookieManager.setAcceptCookie(currentWebView.getAcceptFirstPartyCookies());
 
-        // Update the privacy icons.  `true` redraws the icons in the app bar.
-        updatePrivacyIcons(true);
+            // Update the privacy icons.  `true` redraws the icons in the app bar.
+            updatePrivacyIcons(true);
 
-        // Clear the focus from the URL text box.
-        urlEditText.clearFocus();
+            // Clear the focus from the URL text box.
+            urlEditText.clearFocus();
 
-        // Get a handle for the input method manager.
-        InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+            // Get a handle for the input method manager.
+            InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
 
-        // Remove the lint warning below that the input method manager might be null.
-        assert inputMethodManager != null;
+            // Remove the lint warning below that the input method manager might be null.
+            assert inputMethodManager != null;
 
-        // Hide the soft keyboard.
-        inputMethodManager.hideSoftInputFromWindow(currentWebView.getWindowToken(), 0);
+            // Hide the soft keyboard.
+            inputMethodManager.hideSoftInputFromWindow(currentWebView.getWindowToken(), 0);
 
-        // Display the current URL in the URL text box.
-        urlEditText.setText(currentWebView.getUrl());
+            // Display the current URL in the URL text box.
+            urlEditText.setText(currentWebView.getUrl());
 
-        // Highlight the URL text.
-        highlightUrlText();
+            // Highlight the URL text.
+            highlightUrlText();
 
-        // Set the background to indicate the domain settings status.
-        if (currentWebView.getDomainSettingsApplied()) {
-            // Set a green background on the URL relative layout to indicate that custom domain settings are being used. The deprecated `.getDrawable()` must be used until the minimum API >= 21.
-            if (darkTheme) {
-                urlRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_dark_blue));
+            // Set the background to indicate the domain settings status.
+            if (currentWebView.getDomainSettingsApplied()) {
+                // Set a green background on the URL relative layout to indicate that custom domain settings are being used. The deprecated `.getDrawable()` must be used until the minimum API >= 21.
+                if (darkTheme) {
+                    urlRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_dark_blue));
+                } else {
+                    urlRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_light_green));
+                }
             } else {
-                urlRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_light_green));
+                urlRelativeLayout.setBackground(getResources().getDrawable(R.color.transparent));
             }
-        } else {
-            urlRelativeLayout.setBackground(getResources().getDrawable(R.color.transparent));
         }
     }
 
@@ -4313,6 +4225,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Get a handle for the input method manager.
         InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
 
+        // Instantiate the blocklist helper.
+        BlockListHelper blockListHelper = new BlockListHelper();
+
         // Remove the lint warning below that the input method manager might be null.
         assert inputMethodManager != null;
 
@@ -4563,20 +4478,20 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     // Get the current tab.
                     TabLayout.Tab tab = tabLayout.getTabAt(currentPosition);
 
-                    // Remove the lint warning below that the current tab might be null.
-                    assert tab != null;
-
-                    // Get the custom view from the tab.
-                    View tabView = tab.getCustomView();
-
-                    // Remove the incorrect warning below that the current tab view might be null.
-                    assert tabView != null;
+                    // Check to see if the tab has been populated.
+                    if (tab != null) {
+                        // Get the custom view from the tab.
+                        View tabView = tab.getCustomView();
 
-                    // Get the favorite icon image view from the tab.
-                    ImageView tabFavoriteIconImageView = tabView.findViewById(R.id.favorite_icon_imageview);
+                        // Check to see if the custom tab view has been populated.
+                        if (tabView != null) {
+                            // Get the favorite icon image view from the tab.
+                            ImageView tabFavoriteIconImageView = tabView.findViewById(R.id.favorite_icon_imageview);
 
-                    // Display the favorite icon in the tab.
-                    tabFavoriteIconImageView.setImageBitmap(Bitmap.createScaledBitmap(icon, 64, 64, true));
+                            // Display the favorite icon in the tab.
+                            tabFavoriteIconImageView.setImageBitmap(Bitmap.createScaledBitmap(icon, 64, 64, true));
+                        }
+                    }
                 }
             }
 
@@ -4672,7 +4587,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Show the main content relative layout.
                 mainContentRelativeLayout.setVisibility(View.VISIBLE);
 
-                // Apply the appropriate full screen mode the `SYSTEM_UI` flags.
+                // Apply the appropriate full screen mode flags.
                 if (fullScreenBrowsingModeEnabled && inFullScreenBrowsingMode) {  // Privacy Browser is currently in full screen browsing mode.
                     // Hide the app bar if specified.
                     if (hideAppBar) {
@@ -4731,7 +4646,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         nestedScrollWebView.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.
             // The deprecated `shouldOverrideUrlLoading` must be used until API >= 24.
-            @SuppressWarnings("deprecation")
             @Override
             public boolean shouldOverrideUrlLoading(WebView view, String url) {
                 if (url.startsWith("http")) {  // Load the URL in Privacy Browser.
@@ -4805,7 +4719,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             }
 
             // Check requests against the block lists.  The deprecated `shouldInterceptRequest()` must be used until minimum API >= 21.
-            @SuppressWarnings("deprecation")
             @Override
             public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
                 // Get a handle for the navigation view.
@@ -4817,6 +4730,15 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Get a handle for the navigation requests menu item.  The menu is 0 based.
                 MenuItem navigationRequestsMenuItem = navigationMenu.getItem(6);
 
+                // Get handles for the options menu items.
+                MenuItem blocklistsMenuItem = optionsMenu.findItem(R.id.blocklists);
+                MenuItem easyListMenuItem = optionsMenu.findItem(R.id.easylist);
+                MenuItem easyPrivacyMenuItem = optionsMenu.findItem(R.id.easyprivacy);
+                MenuItem fanboysAnnoyanceListMenuItem = optionsMenu.findItem(R.id.fanboys_annoyance_list);
+                MenuItem fanboysSocialBlockingListMenuItem = optionsMenu.findItem(R.id.fanboys_social_blocking_list);
+                MenuItem ultraPrivacyMenuItem = optionsMenu.findItem(R.id.ultraprivacy);
+                MenuItem blockAllThirdPartyRequestsMenuItem = optionsMenu.findItem(R.id.block_all_third_party_requests);
+
                 // Create an empty web resource response to be used if the resource request is blocked.
                 WebResourceResponse emptyWebResourceResponse = new WebResourceResponse("text/plain", "utf8", new ByteArrayInputStream("".getBytes()));
 
@@ -5078,11 +5000,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             // Handle HTTP authentication requests.
             @Override
             public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) {
-                // Store `handler` so it can be accessed from `onHttpAuthenticationCancel()` and `onHttpAuthenticationProceed()`.
-                httpAuthHandler = handler;
+                // Store the handler.
+                nestedScrollWebView.setHttpAuthHandler(handler);
 
-                // Display the HTTP authentication dialog.
-                DialogFragment httpAuthenticationDialogFragment = HttpAuthenticationDialog.displayDialog(host, realm);
+                // Instantiate an HTTP authentication dialog.
+                DialogFragment httpAuthenticationDialogFragment = HttpAuthenticationDialog.displayDialog(host, realm, nestedScrollWebView.getWebViewFragmentId());
+
+                // Show the HTTP authentication dialog.
                 httpAuthenticationDialogFragment.show(getSupportFragmentManager(), getString(R.string.http_authentication));
             }
 
@@ -5123,13 +5047,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     new GetHostIpAddresses(activity, getSupportFragmentManager(), nestedScrollWebView).execute(currentUri.getHost());
 
                     // Apply any custom domain settings if the URL was loaded by navigating history.
-                    if (navigatingHistory) {
+                    if (nestedScrollWebView.getNavigatingHistory()) {
+                        // Reset navigating history.
+                        nestedScrollWebView.setNavigatingHistory(false);
+
                         // Apply the domain settings.
                         boolean userAgentChanged = applyDomainSettings(nestedScrollWebView, url, true, false);
 
-                        // Reset `navigatingHistory`.
-                        navigatingHistory = false;
-
                         // Manually load the URL if the user agent has changed, which will have caused the previous URL to be reloaded.
                         if (userAgentChanged) {
                             loadUrl(url);
@@ -5204,6 +5128,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                     // Manually delete cache folders.
                     try {
+                        // Get the application's private data directory, which will be something like `/data/user/0/com.stoutner.privacybrowser.standard`,
+                        // which links to `/data/data/com.stoutner.privacybrowser.standard`.
+                        String privateDataDirectoryString = getApplicationInfo().dataDir;
+
                         // Delete the main cache directory.
                         Runtime.getRuntime().exec("rm -rf " + privateDataDirectoryString + "/cache");
 
@@ -5287,11 +5215,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                         handler.proceed();
                     }
                 } else {  // Either there isn't a pinned SSL certificate or it doesn't match the current website certificate.
-                    // Store `handler` so it can be accesses from `onSslErrorCancel()` and `onSslErrorProceed()`.
-                    sslErrorHandler = handler;  // TODO.  We need to pass this in instead of using a static variable.  Because multiple could be displayed at once from different tabs.
+                    // Store the SSL error handler.
+                    nestedScrollWebView.setSslErrorHandler(handler);
+
+                    // Instantiate an SSL certificate error alert dialog.
+                    DialogFragment sslCertificateErrorDialogFragment = SslCertificateErrorDialog.displayDialog(error, nestedScrollWebView.getWebViewFragmentId());
 
-                    // Display the SSL error `AlertDialog`.
-                    DialogFragment sslCertificateErrorDialogFragment = SslCertificateErrorDialog.displayDialog(error);
+                    // Show the SSL certificate error dialog.
                     sslCertificateErrorDialogFragment.show(getSupportFragmentManager(), getString(R.string.ssl_certificate_error));
                 }
             }
diff --git a/app/src/main/java/com/stoutner/privacybrowser/adapters/AboutPagerAdapter.java b/app/src/main/java/com/stoutner/privacybrowser/adapters/AboutPagerAdapter.java
new file mode 100644 (file)
index 0000000..0d9a158
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright © 2016-2019 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+ *
+ * Privacy Browser is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Privacy Browser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Privacy Browser.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.stoutner.privacybrowser.adapters;
+
+import android.content.Context;
+
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentPagerAdapter;
+
+import com.stoutner.privacybrowser.R;
+import com.stoutner.privacybrowser.fragments.AboutTabFragment;
+
+public class AboutPagerAdapter extends FragmentPagerAdapter {
+    // Define the class variable to store the blocklist versions.
+    private Context context;
+    private String[] blocklistVersions;
+
+    public AboutPagerAdapter(FragmentManager fragmentManager, Context context, String[] blocklistVersions) {
+        // Run the default commands.
+        super(fragmentManager);
+
+        // Store the context in a class variable.
+        this.context = context;
+
+        // Store the blocklist versions in a class variable.
+        this.blocklistVersions = blocklistVersions;
+    }
+
+    @Override
+    // Get the count of the number of tabs.
+    public int getCount() {
+        return 7;
+    }
+
+    @Override
+    // Get the name of each tab.  Tab numbers start at 0.
+    public CharSequence getPageTitle(int tab) {
+        switch (tab) {
+            case 0:
+                return context.getResources().getString(R.string.version);
+
+            case 1:
+                return context.getResources().getString(R.string.permissions);
+
+            case 2:
+                return context.getResources().getString(R.string.privacy_policy);
+
+            case 3:
+                return context.getResources().getString(R.string.changelog);
+
+            case 4:
+                return context.getResources().getString(R.string.licenses);
+
+            case 5:
+                return context.getResources().getString(R.string.contributors);
+
+            case 6:
+                return context.getResources().getString(R.string.links);
+
+            default:
+                return "";
+        }
+    }
+
+    @Override
+    // Setup each tab.
+    public Fragment getItem(int tabNumber) {
+        return AboutTabFragment.createTab(tabNumber, blocklistVersions);
+    }
+}
\ No newline at end of file
index e4f558b..e163ac5 100644 (file)
@@ -64,7 +64,7 @@ public class AboutViewSourceDialog extends DialogFragment {
         dialogBuilder.setMessage(R.string.about_view_source_message);
 
         // Create an alert dialog from the alert dialog builder.
-        final AlertDialog alertDialog = dialogBuilder.create();
+        AlertDialog alertDialog = dialogBuilder.create();
 
         // Disable screenshots if not allowed.
         if (!allowScreenshots) {
@@ -75,7 +75,7 @@ public class AboutViewSourceDialog extends DialogFragment {
             alertDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
         }
 
-        // `onCreateDialog` requires the return of an `AlertDialog`.
+        // Return the alert dialog.
         return alertDialog;
     }
 }
index c04bf8f..7ee7dd0 100644 (file)
@@ -22,7 +22,6 @@ package com.stoutner.privacybrowser.dialogs;
 import android.annotation.SuppressLint;
 import android.app.AlertDialog;
 import android.app.Dialog;
-import android.content.Context;
 import android.content.DialogInterface;
 import android.content.SharedPreferences;
 import android.os.Bundle;
@@ -34,6 +33,7 @@ import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.WindowManager;
+import android.webkit.HttpAuthHandler;
 import android.widget.EditText;
 import android.widget.TextView;
 
@@ -41,34 +41,31 @@ import androidx.annotation.NonNull;
 import androidx.fragment.app.DialogFragment;  // The AndroidX dialog fragment must be used or an error is produced on API <=22.
 
 import com.stoutner.privacybrowser.R;
+import com.stoutner.privacybrowser.activities.MainWebViewActivity;
+import com.stoutner.privacybrowser.fragments.WebViewTabFragment;
+import com.stoutner.privacybrowser.views.NestedScrollWebView;
 
 public class HttpAuthenticationDialog extends DialogFragment{
-    // `httpAuthenticationListener` is used in `onAttach()` and `onCreateDialog()`
-    private HttpAuthenticationListener httpAuthenticationListener;
+    // Define the class variables.
+    private EditText usernameEditText;
+    private EditText passwordEditText;
 
-    // The public interface is used to send information back to the parent activity.
-    public interface HttpAuthenticationListener {
-        void onHttpAuthenticationCancel();
-
-        void onHttpAuthenticationProceed(DialogFragment dialogFragment);
-    }
-
-    public void onAttach(Context context) {
-        super.onAttach(context);
-
-        // Get a handle for `httpAuthenticationListener` from `context`.
-        httpAuthenticationListener = (HttpAuthenticationListener) context;
-    }
-
-    public static HttpAuthenticationDialog displayDialog(String host, String realm) {
-        // Store the strings in a `Bundle`.
+    public static HttpAuthenticationDialog displayDialog(String host, String realm, long webViewFragmentId) {
+        // Create an arguments bundle.
         Bundle argumentsBundle = new Bundle();
-        argumentsBundle.putString("Host", host);
-        argumentsBundle.putString("Realm", realm);
 
-        // Add `argumentsBundle` to this instance of `HttpAuthenticationDialog`.
+        // Store the variables in the bundle.
+        argumentsBundle.putString("host", host);
+        argumentsBundle.putString("realm", realm);
+        argumentsBundle.putLong("webview_fragment_id", webViewFragmentId);
+
+        // Create a new instance of the HTTP authentication dialog.
         HttpAuthenticationDialog thisHttpAuthenticationDialog = new HttpAuthenticationDialog();
+
+        // Add the arguments bundle to the new dialog.
         thisHttpAuthenticationDialog.setArguments(argumentsBundle);
+
+        // Return the new dialog.
         return thisHttpAuthenticationDialog;
     }
 
@@ -77,12 +74,34 @@ public class HttpAuthenticationDialog extends DialogFragment{
     @Override
     @NonNull
     public Dialog onCreateDialog(Bundle savedInstanceState) {
-        // Remove the incorrect lint warnings that `getString()` might be null.
-        assert getArguments() != null;
+        // Get a handle for the arguments.
+        Bundle arguments = getArguments();
+
+        // Remove the incorrect lint warning below that arguments might be null.
+        assert arguments != null;
 
-        // Get the host and realm variables from the bundle.
-        String httpAuthHost = getArguments().getString("Host");
-        String httpAuthRealm = getArguments().getString("Realm");
+        // Get the variables from the bundle.
+        String httpAuthHost = arguments.getString("host");
+        String httpAuthRealm = arguments.getString("realm");
+        long webViewFragmentId = arguments.getLong("webview_fragment_id");
+
+        // Get the current position of this WebView fragment.
+        int webViewPosition = MainWebViewActivity.webViewPagerAdapter.getPositionForId(webViewFragmentId);
+
+        // Get the WebView tab fragment.
+        WebViewTabFragment webViewTabFragment = MainWebViewActivity.webViewPagerAdapter.getPageFragment(webViewPosition);
+
+        // Get the fragment view.
+        View fragmentView = webViewTabFragment.getView();
+
+        // Remove the incorrect lint warning below that the fragment view might be null.
+        assert fragmentView != null;
+
+        // Get a handle for the current WebView.
+        NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
+
+        // Get a handle for the HTTP authentication handler.
+        HttpAuthHandler httpAuthHandler = nestedScrollWebView.getHttpAuthHandler();
 
         // Remove the incorrect lint warning that `getActivity()` might be null.
         assert getActivity() != null;
@@ -121,16 +140,22 @@ public class HttpAuthenticationDialog extends DialogFragment{
         // Set the layout.  The parent view is `null` because it will be assigned by `AlertDialog`.
         dialogBuilder.setView(layoutInflater.inflate(R.layout.http_authentication_dialog, null));
 
-        // Setup the negative button.
+        // Setup the close button.
         dialogBuilder.setNegativeButton(R.string.close, (DialogInterface dialog, int which) -> {
-            // Call `onHttpAuthenticationCancel()` and return the `DialogFragment` to the parent activity.
-            httpAuthenticationListener.onHttpAuthenticationCancel();
+            // Cancel the HTTP authentication request.
+            httpAuthHandler.cancel();
+
+            // Reset the HTTP authentication handler.
+            nestedScrollWebView.resetHttpAuthHandler();
         });
 
-        // Setup the positive button.
+        // Setup the proceed button.
         dialogBuilder.setPositiveButton(R.string.proceed, (DialogInterface dialog, int which) -> {
-            // Call `onHttpAuthenticationProceed()` and return the `DialogFragment` to the parent activity.
-            httpAuthenticationListener.onHttpAuthenticationProceed(HttpAuthenticationDialog.this);
+            // Send the login information
+            login(httpAuthHandler);
+
+            // Reset the HTTP authentication handler.
+            nestedScrollWebView.resetHttpAuthHandler();
         });
 
         // Create an alert dialog from the alert dialog builder.
@@ -144,27 +169,25 @@ public class HttpAuthenticationDialog extends DialogFragment{
             alertDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
         }
 
-        // Show the keyboard when the `AlertDialog` is displayed on the screen.
+        // Show the keyboard when the alert dialog is displayed on the screen.
         alertDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
 
         // The alert dialog needs to be shown before the contents can be modified.
         alertDialog.show();
 
-        // Get handles for the views in `alertDialog`.
+        // Get handles for the views.
         TextView realmTextView = alertDialog.findViewById(R.id.http_authentication_realm);
         TextView hostTextView = alertDialog.findViewById(R.id.http_authentication_host);
-        EditText usernameEditText = alertDialog.findViewById(R.id.http_authentication_username);
-        EditText passwordEditText = alertDialog.findViewById(R.id.http_authentication_password);
+        usernameEditText = alertDialog.findViewById(R.id.http_authentication_username);
+        passwordEditText = alertDialog.findViewById(R.id.http_authentication_password);
 
         // Set the realm text.
         realmTextView.setText(httpAuthRealm);
 
         // Set the realm text color according to the theme.  The deprecated `.getColor()` must be used until API >= 23.
         if (darkTheme) {
-            //noinspection deprecation
             realmTextView.setTextColor(getResources().getColor(R.color.gray_300));
         } else {
-            //noinspection deprecation
             realmTextView.setTextColor(getResources().getColor(R.color.black));
         }
 
@@ -177,10 +200,8 @@ public class HttpAuthenticationDialog extends DialogFragment{
 
         // Set `blueColorSpan` according to the theme.  The deprecated `getColor()` must be used until API >= 23.
         if (darkTheme) {
-            //noinspection deprecation
             blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.blue_400));
         } else {
-            //noinspection deprecation
             blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.blue_700));
         }
 
@@ -194,10 +215,10 @@ public class HttpAuthenticationDialog extends DialogFragment{
         usernameEditText.setOnKeyListener((View view, int keyCode, KeyEvent event) -> {
             // If the event is a key-down on the `enter` key, call `onHttpAuthenticationProceed()`.
             if ((keyCode == KeyEvent.KEYCODE_ENTER) && (event.getAction() == KeyEvent.ACTION_DOWN)) {
-                // Trigger `onHttpAuthenticationProceed` and return the `DialogFragment` to the parent activity.
-                httpAuthenticationListener.onHttpAuthenticationProceed(HttpAuthenticationDialog.this);
+                // Send the login information.
+                login(httpAuthHandler);
 
-                // Manually dismiss the `AlertDialog`.
+                // Manually dismiss the alert dialog.
                 alertDialog.dismiss();
 
                 // Consume the event.
@@ -211,10 +232,10 @@ public class HttpAuthenticationDialog extends DialogFragment{
         passwordEditText.setOnKeyListener((View view, int keyCode, KeyEvent event) -> {
             // If the event is a key-down on the `enter` key, call `onHttpAuthenticationProceed()`.
             if ((keyCode == KeyEvent.KEYCODE_ENTER) && (event.getAction() == KeyEvent.ACTION_DOWN)) {
-                // Trigger `onHttpAuthenticationProceed` and return the `DialogFragment` to the parent activity.
-                httpAuthenticationListener.onHttpAuthenticationProceed(HttpAuthenticationDialog.this);
+                // Send the login information.
+                login(httpAuthHandler);
 
-                // Manually dismiss the `AlertDialog`.
+                // Manually dismiss the alert dialog.
                 alertDialog.dismiss();
 
                 // Consume the event.
@@ -224,7 +245,12 @@ public class HttpAuthenticationDialog extends DialogFragment{
             }
         });
 
-        // `onCreateDialog()` requires the return of an `AlertDialog`.
+        // Return the alert dialog.
         return alertDialog;
     }
+
+    private void login(HttpAuthHandler httpAuthHandler) {
+        // Send the login information.
+        httpAuthHandler.proceed(usernameEditText.getText().toString(), passwordEditText.getText().toString());
+    }
 }
\ No newline at end of file
index fbc3293..fbf1f90 100644 (file)
@@ -428,17 +428,15 @@ public class PinnedMismatchDialog extends DialogFragment {
             boolean darkTheme = sharedPreferences.getBoolean("dark_theme", false);
 
             // Create a red foreground color span.  The deprecated `getResources().getColor` must be used until the minimum API >= 23.
-            @SuppressWarnings("deprecation") ForegroundColorSpan redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700));
+            ForegroundColorSpan redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700));
 
             // Create a blue foreground color span.
             ForegroundColorSpan blueColorSpan;
 
             // Set the blue color span according to the theme.  The deprecated `getResources().getColor` must be used until the minimum API >= 23.
             if (darkTheme) {
-                //noinspection deprecation
                 blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.blue_400));
             } else {
-                //noinspection deprecation
                 blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.blue_700));
             }
 
index 5f46d4f..7d8e0b6 100644 (file)
@@ -23,7 +23,6 @@ import android.annotation.SuppressLint;
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.Dialog;
-import android.content.Context;
 import android.content.DialogInterface;
 import android.content.SharedPreferences;
 import android.net.Uri;
@@ -36,13 +35,18 @@ import android.text.SpannableStringBuilder;
 import android.text.Spanned;
 import android.text.style.ForegroundColorSpan;
 import android.view.LayoutInflater;
+import android.view.View;
 import android.view.WindowManager;
+import android.webkit.SslErrorHandler;
 import android.widget.TextView;
 
 import androidx.annotation.NonNull;
 import androidx.fragment.app.DialogFragment;  // The AndroidX dialog fragment must be used or an error is produced on API <=22.
 
 import com.stoutner.privacybrowser.R;
+import com.stoutner.privacybrowser.activities.MainWebViewActivity;
+import com.stoutner.privacybrowser.fragments.WebViewTabFragment;
+import com.stoutner.privacybrowser.views.NestedScrollWebView;
 
 import java.lang.ref.WeakReference;
 import java.net.InetAddress;
@@ -51,25 +55,7 @@ import java.text.DateFormat;
 import java.util.Date;
 
 public class SslCertificateErrorDialog extends DialogFragment {
-    // `sslCertificateErrorListener` is used in `onAttach` and `onCreateDialog`.
-    private SslCertificateErrorListener sslCertificateErrorListener;
-
-    // The public interface is used to send information back to the parent activity.
-    public interface SslCertificateErrorListener {
-        void onSslErrorCancel();
-
-        void onSslErrorProceed();
-    }
-
-    public void onAttach(Context context) {
-        // Run the default commands.
-        super.onAttach(context);
-
-        // Get a handle for `SslCertificateErrorListener` from the launching context.
-        sslCertificateErrorListener = (SslCertificateErrorListener) context;
-    }
-
-    public static SslCertificateErrorDialog displayDialog(SslError error) {
+    public static SslCertificateErrorDialog displayDialog(SslError error, long webViewFragmentId) {
         // Get the various components of the SSL error message.
         int primaryErrorIntForBundle = error.getPrimaryError();
         String urlWithErrorForBundle = error.getUrl();
@@ -83,45 +69,73 @@ public class SslCertificateErrorDialog extends DialogFragment {
         Date startDateForBundle = sslCertificate.getValidNotBeforeDate();
         Date endDateForBundle = sslCertificate.getValidNotAfterDate();
 
-        // Store the SSL error message components in a `Bundle`.
+        // Create an arguments bundle.
         Bundle argumentsBundle = new Bundle();
-        argumentsBundle.putInt("PrimaryErrorInt", primaryErrorIntForBundle);
-        argumentsBundle.putString("UrlWithError", urlWithErrorForBundle);
-        argumentsBundle.putString("IssuedToCName", issuedToCNameForBundle);
-        argumentsBundle.putString("IssuedToOName", issuedToONameForBundle);
-        argumentsBundle.putString("IssuedToUName", issuedToUNameForBundle);
-        argumentsBundle.putString("IssuedByCName", issuedByCNameForBundle);
-        argumentsBundle.putString("IssuedByOName", issuedByONameForBundle);
-        argumentsBundle.putString("IssuedByUName", issuedByUNameForBundle);
-        argumentsBundle.putString("StartDate", DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(startDateForBundle));
-        argumentsBundle.putString("EndDate", DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(endDateForBundle));
-
-        // Add `argumentsBundle` to this instance of `SslCertificateErrorDialog`.
+
+        // Store the SSL error message components in a `Bundle`.
+        argumentsBundle.putInt("primary_error_int", primaryErrorIntForBundle);
+        argumentsBundle.putString("url_with_error", urlWithErrorForBundle);
+        argumentsBundle.putString("issued_to_cname", issuedToCNameForBundle);
+        argumentsBundle.putString("issued_to_oname", issuedToONameForBundle);
+        argumentsBundle.putString("issued_to_uname", issuedToUNameForBundle);
+        argumentsBundle.putString("issued_by_cname", issuedByCNameForBundle);
+        argumentsBundle.putString("issued_by_oname", issuedByONameForBundle);
+        argumentsBundle.putString("issued_by_uname", issuedByUNameForBundle);
+        argumentsBundle.putString("start_date", DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(startDateForBundle));
+        argumentsBundle.putString("end_date", DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(endDateForBundle));
+        argumentsBundle.putLong("webview_fragment_id", webViewFragmentId);
+
+        // Create a new instance of the SSL certificate error dialog.
         SslCertificateErrorDialog thisSslCertificateErrorDialog = new SslCertificateErrorDialog();
+
+        // Add the arguments bundle to the new dialog.
         thisSslCertificateErrorDialog.setArguments(argumentsBundle);
+
+        // Return the new dialog.
         return thisSslCertificateErrorDialog;
     }
 
     // `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`.
     @SuppressLint("InflateParams")
-    @SuppressWarnings("deprecation")
     @Override
     @NonNull
     public Dialog onCreateDialog(Bundle savedInstanceState) {
-        // Remove the incorrect lint warning that `getArguments()` might be null.
-        assert getArguments() != null;
-
-        // Get the components of the SSL error message from the bundle.
-        int primaryErrorInt = getArguments().getInt("PrimaryErrorInt");
-        String urlWithErrors = getArguments().getString("UrlWithError");
-        String issuedToCName = getArguments().getString("IssuedToCName");
-        String issuedToOName = getArguments().getString("IssuedToOName");
-        String issuedToUName = getArguments().getString("IssuedToUName");
-        String issuedByCName = getArguments().getString("IssuedByCName");
-        String issuedByOName = getArguments().getString("IssuedByOName");
-        String issuedByUName = getArguments().getString("IssuedByUName");
-        String startDate = getArguments().getString("StartDate");
-        String endDate = getArguments().getString("EndDate");
+        // Get a handle for the arguments.
+        Bundle arguments = getArguments();
+
+        // Remove the incorrect lint warning that the arguments might be null.
+        assert arguments != null;
+
+        // Get the variables from the bundle.
+        int primaryErrorInt = arguments.getInt("primary_error_int");
+        String urlWithErrors = arguments.getString("url_with_error");
+        String issuedToCName = arguments.getString("issued_to_cname");
+        String issuedToOName = arguments.getString("issued_to_oname");
+        String issuedToUName = arguments.getString("issued_to_uname");
+        String issuedByCName = arguments.getString("issued_by_cname");
+        String issuedByOName = arguments.getString("issued_by_oname");
+        String issuedByUName = arguments.getString("issued_by_uname");
+        String startDate = arguments.getString("start_date");
+        String endDate = arguments.getString("end_date");
+        long webViewFragmentId = arguments.getLong("webview_fragment_id");
+
+        // Get the current position of this WebView fragment.
+        int webViewPosition = MainWebViewActivity.webViewPagerAdapter.getPositionForId(webViewFragmentId);
+
+        // Get the WebView tab fragment.
+        WebViewTabFragment webViewTabFragment = MainWebViewActivity.webViewPagerAdapter.getPageFragment(webViewPosition);
+
+        // Get the fragment view.
+        View fragmentView = webViewTabFragment.getView();
+
+        // Remove the incorrect lint warning below that the fragment view might be null.
+        assert fragmentView != null;
+
+        // Get a handle for the current WebView.
+        NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
+
+        // Get a handle for the SSL error handler.
+        SslErrorHandler sslErrorHandler = nestedScrollWebView.getSslErrorHandler();
 
         // Remove the incorrect lint warning that `getActivity()` might be null.
         assert getActivity() != null;
@@ -160,11 +174,29 @@ public class SslCertificateErrorDialog extends DialogFragment {
         // Set the view.  The parent view is `null` because it will be assigned by `AlertDialog`.
         dialogBuilder.setView(layoutInflater.inflate(R.layout.ssl_certificate_error, null));
 
-        // Set a listener on the negative button.
-        dialogBuilder.setNegativeButton(R.string.cancel, (DialogInterface dialog, int which) -> sslCertificateErrorListener.onSslErrorCancel());
+        // Set a listener on the cancel button.
+        dialogBuilder.setNegativeButton(R.string.cancel, (DialogInterface dialog, int which) -> {
+            // Check to make sure the SSL error handler is not null.  This might happen if multiple dialogs are displayed at once.
+            if (sslErrorHandler != null) {
+                // Cancel the request.
+                sslErrorHandler.cancel();
+
+                // Reset the SSL error handler.
+                nestedScrollWebView.resetSslErrorHandler();
+            }
+        });
+
+        // Set a listener on the proceed button.
+        dialogBuilder.setPositiveButton(R.string.proceed, (DialogInterface dialog, int which) -> {
+            // Check to make sure the SSL error handler is not null.  This might happen if multiple dialogs are displayed at once.
+            if (sslErrorHandler != null) {
+                // Cancel the request.
+                sslErrorHandler.proceed();
 
-        // Set a listener on the positive button.
-        dialogBuilder.setPositiveButton(R.string.proceed, (DialogInterface dialog, int which) -> sslCertificateErrorListener.onSslErrorProceed());
+                // Reset the SSL error handler.
+                nestedScrollWebView.resetSslErrorHandler();
+            }
+        });
 
 
         // Create an alert dialog from the alert dialog builder.
@@ -222,17 +254,15 @@ public class SslCertificateErrorDialog extends DialogFragment {
         SpannableStringBuilder endDateStringBuilder = new SpannableStringBuilder((endDateLabel + endDate));
 
         // Create a red foreground color span.  The deprecated `getResources().getColor` must be used until the minimum API >= 23.
-        @SuppressWarnings("deprecation") ForegroundColorSpan redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700));
+        ForegroundColorSpan redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700));
 
         // Create a blue `ForegroundColorSpan`.
         ForegroundColorSpan blueColorSpan;
 
         // Set a blue color span according to the theme.  The deprecated `getResources().getColor` must be used until the minimum API >= 23.
         if (darkTheme) {
-            //noinspection deprecation
             blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.blue_400));
         } else {
-            //noinspection deprecation
             blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.blue_700));
         }
 
@@ -391,10 +421,8 @@ public class SslCertificateErrorDialog extends DialogFragment {
 
             // Set the blue color span according to the theme.  The deprecated `getColor()` must be used until the minimum API >= 23.
             if (darkTheme) {
-                //noinspection deprecation
                 blueColorSpan = new ForegroundColorSpan(activity.getResources().getColor(R.color.blue_400));
             } else {
-                //noinspection deprecation
                 blueColorSpan = new ForegroundColorSpan(activity.getResources().getColor(R.color.blue_700));
             }
 
index 8c48825..4ff9a28 100644 (file)
@@ -41,7 +41,6 @@ import androidx.fragment.app.Fragment;
 
 import com.stoutner.privacybrowser.BuildConfig;
 import com.stoutner.privacybrowser.R;
-import com.stoutner.privacybrowser.activities.MainWebViewActivity;
 
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
@@ -54,20 +53,23 @@ import java.text.DateFormat;
 import java.util.Date;
 
 public class AboutTabFragment extends Fragment {
-    // Track the current tab number.
+    // Declare the class variables.
     private int tabNumber;
+    private String[] blocklistVersions;
 
-    // Store the tab number in the arguments bundle.
-    public static AboutTabFragment createTab(int tab) {
+    public static AboutTabFragment createTab(int tabNumber, String[] blocklistVersions) {
         // Create a bundle.
-        Bundle bundle = new Bundle();
+        Bundle argumentsBundle = new Bundle();
 
         // Store the tab number in the bundle.
-        bundle.putInt("Tab", tab);
+        argumentsBundle.putInt("tab_number", tabNumber);
+        argumentsBundle.putStringArray("blocklist_versions", blocklistVersions);
 
-        // Add the bundle to the fragment.
+        // Create a new instance of the tab fragment.
         AboutTabFragment aboutTabFragment = new AboutTabFragment();
-        aboutTabFragment.setArguments(bundle);
+
+        // Add the arguments bundle to the fragment.
+        aboutTabFragment.setArguments(argumentsBundle);
 
         // Return the new fragment.
         return aboutTabFragment;
@@ -78,11 +80,15 @@ public class AboutTabFragment extends Fragment {
         // Run the default commands.
         super.onCreate(savedInstanceState);
 
-        // Remove the lint warning that `getArguments()` might be null.
-        assert getArguments() != null;
+        // Get a handle for the arguments.
+        Bundle arguments = getArguments();
+
+        // Remove the incorrect lint warning below that arguments might be null.
+        assert arguments != null;
 
-        // Store the tab number in a class variable.
-        tabNumber = getArguments().getInt("Tab");
+        // Store the arguments in class variables.
+        tabNumber = getArguments().getInt("tab_number");
+        blocklistVersions = getArguments().getStringArray("blocklist_versions");
     }
 
     @Override
@@ -133,7 +139,7 @@ public class AboutTabFragment extends Fragment {
             TextView certificateSignatureAlgorithmTextView = tabLayout.findViewById(R.id.certificate_signature_algorithm);
 
             // Setup the labels.
-            String version = getString(R.string.version) + " " + BuildConfig.VERSION_NAME + " (" + getString(R.string.version_code) + " " + Integer.toString(BuildConfig.VERSION_CODE) + ")";
+            String version = getString(R.string.version) + " " + BuildConfig.VERSION_NAME + " (" + getString(R.string.version_code) + " " + BuildConfig.VERSION_CODE + ")";
             String brandLabel = getString(R.string.brand) + "  ";
             String manufacturerLabel = getString(R.string.manufacturer) + "  ";
             String modelLabel = getString(R.string.model) + "  ";
@@ -167,7 +173,7 @@ public class AboutTabFragment extends Fragment {
             String device = Build.DEVICE;
             String bootloader = Build.BOOTLOADER;
             String radio = Build.getRadioVersion();
-            String android = Build.VERSION.RELEASE + " (" + getString(R.string.api) + " " + Integer.toString(Build.VERSION.SDK_INT) + ")";
+            String android = Build.VERSION.RELEASE + " (" + getString(R.string.api) + " " + Build.VERSION.SDK_INT + ")";
             String build = Build.DISPLAY;
             // Select the substring that begins after `Chrome/` and goes until the next ` `.
             String webView = userAgentString.substring(userAgentString.indexOf("Chrome/") + 7, userAgentString.indexOf(" ", userAgentString.indexOf("Chrome/")));
@@ -199,21 +205,19 @@ public class AboutTabFragment extends Fragment {
             SpannableStringBuilder androidStringBuilder = new SpannableStringBuilder(androidLabel + android);
             SpannableStringBuilder buildStringBuilder = new SpannableStringBuilder(buildLabel + build);
             SpannableStringBuilder webViewStringBuilder = new SpannableStringBuilder(webViewLabel + webView);
-            SpannableStringBuilder easyListStringBuilder = new SpannableStringBuilder(easyListLabel + MainWebViewActivity.easyListVersion);
-            SpannableStringBuilder easyPrivacyStringBuilder = new SpannableStringBuilder(easyPrivacyLabel + MainWebViewActivity.easyPrivacyVersion);
-            SpannableStringBuilder fanboyAnnoyanceStringBuilder = new SpannableStringBuilder(fanboyAnnoyanceLabel + MainWebViewActivity.fanboysAnnoyanceVersion);
-            SpannableStringBuilder fanboySocialStringBuilder = new SpannableStringBuilder(fanboySocialLabel + MainWebViewActivity.fanboysSocialVersion);
-            SpannableStringBuilder ultraPrivacyStringBuilder = new SpannableStringBuilder(ultraPrivacyLabel + MainWebViewActivity.ultraPrivacyVersion);
+            SpannableStringBuilder easyListStringBuilder = new SpannableStringBuilder(easyListLabel + blocklistVersions[0]);
+            SpannableStringBuilder easyPrivacyStringBuilder = new SpannableStringBuilder(easyPrivacyLabel + blocklistVersions[1]);
+            SpannableStringBuilder fanboyAnnoyanceStringBuilder = new SpannableStringBuilder(fanboyAnnoyanceLabel + blocklistVersions[2]);
+            SpannableStringBuilder fanboySocialStringBuilder = new SpannableStringBuilder(fanboySocialLabel + blocklistVersions[3]);
+            SpannableStringBuilder ultraPrivacyStringBuilder = new SpannableStringBuilder(ultraPrivacyLabel + blocklistVersions[4]);
 
             // Create the `blueColorSpan` variable.
             ForegroundColorSpan blueColorSpan;
 
             // Set `blueColorSpan` according to the theme.  We have to use the deprecated `getColor()` until API >= 23.
             if (darkTheme) {
-                //noinspection deprecation
                 blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.blue_400));
             } else {
-                //noinspection deprecation
                 blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.blue_700));
             }
 
@@ -356,7 +360,6 @@ public class AboutTabFragment extends Fragment {
             // Load the tabs according to the theme.
             if (darkTheme) {  // The dark theme is applied.
                 // Set the background color.  The deprecated `.getColor()` must be used until the minimum API >= 23.
-                //noinspection deprecation
                 tabWebView.setBackgroundColor(getResources().getColor(R.color.gray_850));
 
                 switch (tabNumber) {
index 7ef26d6..cf1e56c 100644 (file)
@@ -25,6 +25,8 @@ import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
+import android.webkit.HttpAuthHandler;
+import android.webkit.SslErrorHandler;
 import android.webkit.WebView;
 
 import androidx.annotation.NonNull;
@@ -52,6 +54,10 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild
     // Keep a copy of the WebView fragment ID.
     private long webViewFragmentId;
 
+    // Store the handlers.
+    private SslErrorHandler sslErrorHandler;
+    private HttpAuthHandler httpAuthHandler;
+
     // Track if domain settings are applied to this nested scroll WebView and, if so, the database ID.
     private boolean domainSettingsApplied;
     private int domainSettingsDatabaseId;
@@ -62,6 +68,9 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild
     // Track the status of first-party cookies.
     private boolean acceptFirstPartyCookies;
 
+    // Track the domain settings JavaScript status.  This can be removed once night mode does not require JavaScript.
+    private boolean domainSettingsJavaScriptEnabled;
+
     // Track the resource requests.
     private ArrayList<String[]> resourceRequests = new ArrayList<>();
     private boolean easyListEnabled;
@@ -100,8 +109,11 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild
     // The ignore pinned domain information tracker.  This is set when a user proceeds past a pinned mismatch dialog to prevent the dialog from showing again until after the domain changes.
     private boolean ignorePinnedDomainInformation;
 
+    // Track navigation of history.
+    private boolean navigatingHistory;
+
     // The default or favorite icon.
-    Bitmap favoriteOrDefaultIcon;
+    private Bitmap favoriteOrDefaultIcon;
 
     // Track night mode.
     private boolean nightMode;
@@ -155,6 +167,40 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild
     }
 
 
+    // SSL error handler.
+    public void setSslErrorHandler(SslErrorHandler sslErrorHandler) {
+        // Store the current SSL error handler.
+        this.sslErrorHandler = sslErrorHandler;
+    }
+
+    public SslErrorHandler getSslErrorHandler() {
+        // Return the current SSL error handler.
+        return sslErrorHandler;
+    }
+
+    public void resetSslErrorHandler() {
+        // Reset the current SSL error handler.
+        sslErrorHandler = null;
+    }
+
+
+    // HTTP authentication handler.
+    public void setHttpAuthHandler(HttpAuthHandler httpAuthHandler) {
+        // Store the current HTTP authentication handler.
+        this.httpAuthHandler = httpAuthHandler;
+    }
+
+    public HttpAuthHandler getHttpAuthHandler() {
+        // Return the current HTTP authentication handler.
+        return httpAuthHandler;
+    }
+
+    public void resetHttpAuthHandler() {
+        // Reset the current HTTP authentication handler.
+        httpAuthHandler = null;
+    }
+
+
     // Domain settings.
     public void setDomainSettingsApplied(boolean applied) {
         // Store the domain settings applied status.
@@ -208,6 +254,18 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild
     }
 
 
+    // Domain settings JavaScript enabled.  This can be removed once night mode does not require JavaScript.
+    public void setDomainSettingsJavaScriptEnabled(boolean status) {
+        // Store the domain settings JavaScript status.
+        domainSettingsJavaScriptEnabled = status;
+    }
+
+    public boolean getDomainSettingsJavaScriptEnabled() {
+        // Return the domain settings JavaScript status.
+        return domainSettingsJavaScriptEnabled;
+    }
+
+
     // Resource requests.
     public void addResourceRequest(String[] resourceRequest) {
         // Add the resource request to the list.
@@ -496,16 +554,29 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild
     }
 
 
-    // Ignore pinned information.  The syntax looks better as written, even if it is always inverted.
+    // Ignore pinned information.
+    public void setIgnorePinnedDomainInformation(boolean status) {
+        // Set the status of the ignore pinned domain information tracker.
+        ignorePinnedDomainInformation = status;
+    }
+
+    // The syntax looks better as written, even if it is always inverted.
     @SuppressWarnings("BooleanMethodIsAlwaysInverted")
     public boolean ignorePinnedDomainInformation() {
         // Return the status of the ignore pinned domain information tracker.
         return ignorePinnedDomainInformation;
     }
 
-    public void setIgnorePinnedDomainInformation(boolean status) {
-        // Set the status of the ignore pinned domain information tracker.
-        ignorePinnedDomainInformation = status;
+
+    // Navigating history.
+    public void setNavigatingHistory(boolean status) {
+        // Set the status of navigating history.
+        navigatingHistory = status;
+    }
+
+    public boolean getNavigatingHistory() {
+        // Return the status of navigating history.
+        return navigatingHistory;
     }
 
 
index a1e0e09..3057c92 100644 (file)
@@ -77,6 +77,7 @@
                                 android:id="@+id/tablayout"
                                 android:layout_height="wrap_content"
                                 android:layout_width="wrap_content"
+                                app:tabIndicatorGravity="top"
                                 app:tabMode="scrollable" />
 
                             <!-- `style="?android:borderlessButtonStyle"` shows a visual indication when the image view is tapped. -->
index bb6de10..27e6277 100644 (file)
@@ -41,7 +41,7 @@
         android:layout_height="3dp"
         android:layout_width="match_parent"
         android:max="100"
-        android:progressTint="@color/yellow_a700"
+        android:progressTint="?attr/progressTintColor"
         android:progressBackgroundTint="@color/transparent"
         android:visibility="gone"
         tools:ignore="UnusedAttribute" />
index 32938c3..c7ec135 100644 (file)
@@ -50,6 +50,8 @@
     <string name="no_title">Kein Titel</string>
     <string name="unrecognized_url">Unbekannte URL:</string>
     <string name="open_with">Öffnen mit</string>
+    <string name="new_tab">Neuer Tab</string>
+    <string name="add_tab">Tab hinzufügen</string>
 
     <!-- Save As. -->
     <string name="save_as">Speichern Unter</string>
     <!-- MainWebViewActivity Navigation Menu. -->
     <string name="navigation_drawer">Navigationspanel</string>
     <string name="navigation">Navigation</string>
+    <string name="close_tab">Tab schließen</string>
     <string name="clear_and_exit">Leeren und verlassen</string>
     <string name="home">Startseite</string>
     <string name="back">Zurück</string>
     <string name="stop">Stop</string>
 
     <!-- Context Menus. -->
+    <string name="open_in_new_tab">In neuem Tab öffnen</string>
     <string name="copy_url">URL kopieren</string>
     <string name="download_url">Download URL</string>
     <string name="email_address">E-Mail-Adresse</string>
         <string name="javascript_preference">JavaScript standardmäßig aktivieren</string>
         <string name="javascript_preference_summary">JavaScript ermöglicht es Websites, Programme (Scripts) auf Ihrem Gerät auszuführen.</string>
         <string name="first_party_cookies_preference">Erstanbieter-Cookies standardmäßig aktivieren</string>
+        <string name="first_party_cookies_preference_summary">Da die Cookie-Einstellungen App-weit gelten, werden, wenn im aktiven Tab Cookies erlaubt sind,
+            auch in den Hintergrund-Tabs Cookies der dort angezeigten Seiten und deren Domains angenommen.
+            Unter Android KitKat (version 4.4.x) wird nicht zwischen Cookies und Drittanbieter-Cookies unterschieden,
+            sodass in dieser Version mit dieser Einstellung beide Cookie-Varianten aktiviert werden.</string>
         <string name="third_party_cookies_preference">Drittanbieter-Cookies standardmäßig aktivieren</string>
         <string name="third_party_cookies_summary">Diese Option benötigt Android Lollipop (Version 5.0) oder höher. Sie hat keine Auswirkungen, wenn Erstanbieter-Cookies deaktiviert sind.</string>
         <string name="dom_storage_preference">DOM-Speicher standardmäßig aktivieren</string>
index b6ec37b..869a7ac 100644 (file)
@@ -46,6 +46,8 @@
     <string name="no_title">Nessun titolo</string>
     <string name="unrecognized_url">URL non riconosciuta:</string>
     <string name="open_with">Apri con</string>
+    <string name="new_tab">Nuova Scheda</string>
+    <string name="add_tab">Aggiungi Scheda</string>
 
     <!-- Save As. -->
     <string name="save_as">Salva come</string>
     <!-- MainWebViewActivity Navigation Menu. -->
     <string name="navigation_drawer">Menù di navigazione</string>
     <string name="navigation">Navigazione</string>
+    <string name="close_tab">Chiudi Scheda</string>
     <string name="clear_and_exit">Elimina dati ed esci</string>
     <string name="home">Home</string>
     <string name="back">Indietro</string>
     <string name="stop">Stop</string>
 
     <!-- Context Menus. -->
+    <string name="open_in_new_tab">Apri in una nuova Scheda</string>
     <string name="copy_url">Copia URL</string>
     <string name="download_url">Scarica URL</string>
     <string name="email_address">Indirizzo Email</string>
         <string name="javascript_preference">Abilita JavaScript</string>
         <string name="javascript_preference_summary">Permetti a JavaScript di eseguire programmi (script) sul dispositivo.</string>
         <string name="first_party_cookies_preference">Abilita cookies proprietari</string>
+        <string name="first_party_cookies_preference_summary">Dal momento che i cookie proprietari sono un\'impostazione a livello di applicazione,
+            quando la scheda attiva ha i cookie proprietari attivati anche ogni richiesta di rete in background da parte delle altre schede includerà i cookie per i relativi domini.
+            Android KitKat (versione 4.4.x) inoltre non distingue tra cookie proprietari e cookie di terze parti e pertanto li abiliterà entrambi con questa impostazione.</string>
         <string name="third_party_cookies_preference">Abilita cookies di terze parti</string>
         <string name="third_party_cookies_summary">Questa impostazione richiede Android Lollipop (version 5.0) o successivo.  Non ha effetti se i cookies proprietari sono disabilitati.</string>
         <string name="dom_storage_preference">Abilita DOM Storage</string>
index c413030..e1d1f13 100644 (file)
@@ -44,6 +44,8 @@
     <string name="no_title">Başlıksız</string>
     <string name="unrecognized_url">Onaylanmayan URL:</string>
     <string name="open_with">Bununla aç</string>
+    <string name="new_tab">Yeni Sekme</string>
+    <string name="add_tab">Sekme Ekle</string>
 
     <!-- Save As. -->
     <string name="save_as">Farklı Kaydet</string>
     <!-- MainWebViewActivity Navigation Menu. -->
     <string name="navigation_drawer">Açılır Menü</string>
     <string name="navigation">Gezinti</string>
+    <string name="close_tab">Sekmeyi Kapa</string>
     <string name="clear_and_exit">Temizle ve Çık</string>
     <string name="home">Anasayfa</string>
     <string name="back">Geri</string>
     <string name="stop">Dur</string>
 
     <!-- Context Menus. -->
+    <string name="open_in_new_tab">Yeni Sekmede Aç</string>
     <string name="copy_url">URL Kopyala</string>
     <string name="download_url">URL İndir</string>
     <string name="email_address">Eposta Adresi</string>
         <string name="javascript_preference">JavaScript\'i varsayılan olarak etkinleştir</string>
         <string name="javascript_preference_summary">JavaScript web sitelerin cihazdaki programları(scriptler) çalıştırmasına izin verir.</string>
         <string name="first_party_cookies_preference">Birinci taraf çerezleri varsayılan olarak etkinleştir</string>
+        <string name="first_party_cookies_preference_summary">Birinci taraf çerezleri uygulama düzeyinde bir ayar olduğundan, aktif sekmede çerezler etkinleştirildiğinde,
+            arka plandaki diğer sekmeler tarafından yapılan herhangi ağ istekleri, domainler için kayıtlı çerezleri de içerecektir.
+            Android KitKat (sürüm 4.4.x), birinci ve üçüncü taraf çerezleri arasında ayrım yapmaz ve bu ayarda ikisini de etkinleştirir.</string>
         <string name="third_party_cookies_preference">Üçüncü taraf çerezleri varsayılan olarak etkinleştir</string>
         <string name="third_party_cookies_summary">Bu ayarı etkinleştirmek için Android Lollipop sürümüne(5.0) ya da daha üst sürümlere sahip olmak gerekir.
             Birinci taraf çerezler devre dışı olduğu takdirde etkisi yoktur.</string>
index daa5deb..63641a1 100644 (file)
 
     <!-- `android:windowTranslucentStatus` makes the system status bar translucent.  When it is specified the root layout should include `android:fitsSystemWindows="true"`. -->
     <style name="PrivacyBrowserLight" parent="Theme.AppCompat.Light.NoActionBar" >
+        <item name="aboutIcon">@drawable/about_light</item>
         <item name="android:windowTranslucentStatus">true</item>
         <item name="addTabIconTintColor">@color/gray_700</item>
         <item name="android:textColorPrimary">@color/primary_text_color_selector_light</item>
         <item name="colorAccent">@color/blue_700</item>
+        <item name="dialogTabLayoutTheme">@style/PrivacyBrowserTabLayoutDialogLight</item>
         <item name="android:textColorHighlight">@color/blue_200</item>
         <item name="mainStatusBarBackground">@color/gray_500</item>
         <item name="navigationHeaderBackground">@color/blue_700</item>
         <item name="appBarTheme">@style/PrivacyBrowserAppBarLight</item>
         <item name="navigationIconTintColor">@color/blue_800</item>
         <item name="findOnPageIconTintColor">@color/blue_800</item>
-        <item name="viewSourceIconTintColor">@color/black</item>
+        <item name="progressTintColor">@color/blue_700</item>
+        <item name="redText">@color/red_a700</item>
         <item name="sslHeader">@color/blue_700</item>
         <item name="sslTitle">@color/blue_900</item>
         <item name="urlHistoryText">@color/black</item>
-        <item name="redText">@color/red_a700</item>
-        <item name="aboutIcon">@drawable/about_light</item>
-        <item name="dialogTabLayoutTheme">@style/PrivacyBrowserTabLayoutDialogLight</item>
+        <item name="viewSourceIconTintColor">@color/black</item>
     </style>
 
     <!-- `windowActionModeOverlay` makes the contextual app bar cover the support app bar. -->
 
     <!-- `android:windowTranslucentStatus` makes the system status bar translucent.  When it is specified the root layout should include `android:fitsSystemWindows="true"`. -->
     <style name="PrivacyBrowserDark" parent="Theme.AppCompat.NoActionBar" >
+        <item name="aboutIcon">@drawable/about_dark</item>
         <item name="android:windowTranslucentStatus">true</item>
         <item name="addTabIconTintColor">@color/gray_400</item>
         <item name="android:textColorPrimary">@color/primary_text_color_selector_dark</item>
         <item name="navigationHeaderBackground">@color/blue_800</item>
         <item name="navigationHeaderTextColor">@color/gray_300</item>
         <item name="appBarTheme">@style/PrivacyBrowserAppBarDark</item>
-        <item name="navigationIconTintColor">@color/blue_600</item>
         <item name="findOnPageIconTintColor">@color/blue_600</item>
-        <item name="viewSourceIconTintColor">@color/gray_300</item>
+        <item name="navigationIconTintColor">@color/blue_600</item>
+        <item name="progressTintColor">@color/blue_600</item>
+        <item name="redText">@color/red_900</item>
         <item name="sslHeader">@color/blue_400</item>
         <item name="sslTitle">@color/blue_700</item>
         <item name="urlHistoryText">@color/gray_200</item>
-        <item name="redText">@color/red_900</item>
-        <item name="aboutIcon">@drawable/about_dark</item>
+        <item name="viewSourceIconTintColor">@color/gray_300</item>
     </style>
 
     <!-- `windowActionModeOverlay` makes the contextual app bar cover the support app bar.  `colorPrimaryDark` goes behind the status bar, which is then darkened by the overlay.-->
index 3eaac49..e0e767e 100644 (file)
@@ -47,6 +47,7 @@
     <attr name="domainSettingsIconTintColor" format="reference" />
     <attr name="findOnPageIconTintColor" format="reference" />
     <attr name="navigationIconTintColor" format="reference" />
+    <attr name="progressTintColor" format="reference" />
     <attr name="viewSourceIconTintColor" format="reference" />
 
     <attr name="listSelectorDrawable" format="reference" />
index 643cee3..26e0c89 100644 (file)
 
     <!-- `android:windowTranslucentStatus` makes the system status bar translucent.  When it is specified the root layout should include `android:fitsSystemWindows="true"`. -->
     <style name="PrivacyBrowserLight" parent="Theme.AppCompat.Light.NoActionBar" >
+        <item name="aboutIcon">@drawable/about_light</item>
         <item name="android:windowTranslucentStatus">true</item>
         <item name="addTabIconTintColor">@color/gray_700</item>
         <item name="android:textColorPrimary">@color/primary_text_color_selector_light</item>
         <item name="colorAccent">@color/blue_700</item>
+        <item name="dialogTabLayoutTheme">@style/PrivacyBrowserTabLayoutDialogLight</item>
         <item name="android:textColorHighlight">@color/blue_200</item>
         <item name="mainStatusBarBackground">@color/gray_500</item>
         <item name="navigationHeaderBackground">@color/blue_700</item>
         <item name="appBarTheme">@style/PrivacyBrowserAppBarLight</item>
         <item name="navigationIconTintColor">@color/blue_800</item>
         <item name="findOnPageIconTintColor">@color/blue_800</item>
-        <item name="viewSourceIconTintColor">@color/black</item>
+        <item name="progressTintColor">@color/blue_700</item>
+        <item name="redText">@color/red_a700</item>
         <item name="sslHeader">@color/blue_700</item>
         <item name="sslTitle">@color/blue_900</item>
         <item name="urlHistoryText">@color/black</item>
-        <item name="redText">@color/red_a700</item>
-        <item name="aboutIcon">@drawable/about_light</item>
-        <item name="dialogTabLayoutTheme">@style/PrivacyBrowserTabLayoutDialogLight</item>
+        <item name="viewSourceIconTintColor">@color/black</item>
     </style>
 
     <!-- `windowActionModeOverlay` makes the contextual app bar cover the support app bar. -->
 
     <!-- `android:windowTranslucentStatus` makes the system status bar translucent.  When it is specified the root layout should include `android:fitsSystemWindows="true"`. -->
     <style name="PrivacyBrowserDark" parent="Theme.AppCompat.NoActionBar" >
+        <item name="aboutIcon">@drawable/about_dark</item>
         <item name="android:windowTranslucentStatus">true</item>
         <item name="addTabIconTintColor">@color/gray_400</item>
         <item name="android:textColorPrimary">@color/primary_text_color_selector_dark</item>
         <item name="navigationHeaderBackground">@color/blue_800</item>
         <item name="navigationHeaderTextColor">@color/gray_300</item>
         <item name="appBarTheme">@style/PrivacyBrowserAppBarDark</item>
-        <item name="navigationIconTintColor">@color/blue_600</item>
         <item name="findOnPageIconTintColor">@color/blue_600</item>
-        <item name="viewSourceIconTintColor">@color/gray_300</item>
+        <item name="navigationIconTintColor">@color/blue_600</item>
+        <item name="progressTintColor">@color/blue_600</item>
+        <item name="redText">@color/red_900</item>
         <item name="sslHeader">@color/blue_400</item>
         <item name="sslTitle">@color/blue_700</item>
         <item name="urlHistoryText">@color/gray_200</item>
-        <item name="redText">@color/red_900</item>
-        <item name="aboutIcon">@drawable/about_dark</item>
+        <item name="viewSourceIconTintColor">@color/gray_300</item>
     </style>
 
     <!-- `windowActionModeOverlay` makes the contextual app bar cover the support app bar.  `colorPrimaryDark` goes behind the status bar, which is then darkened by the overlay.-->
index b4b8049..3832dcd 100644 (file)
@@ -25,7 +25,7 @@ buildscript {
         google()
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:3.3.2'
+        classpath 'com.android.tools.build:gradle:3.4.0'
 
         // NOTE: Do not place your application dependencies here; they belong
         // in the individual module build.gradle files
index fd0f4af..ed71cde 100644 (file)
@@ -1,6 +1,6 @@
-#Tue Jan 15 11:00:04 MST 2019
+#Wed Apr 17 14:57:58 MST 2019
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip