]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/commitdiff
Migrate the remaining classes to Kotlin. https://redmine.stoutner.com/issues/989
authorSoren Stoutner <soren@stoutner.com>
Tue, 11 Apr 2023 22:45:38 +0000 (15:45 -0700)
committerSoren Stoutner <soren@stoutner.com>
Tue, 11 Apr 2023 22:45:38 +0000 (15:45 -0700)
60 files changed:
app/src/main/assets/fr/about_changelog.html
app/src/main/assets/zh-rCN/about_changelog.html
app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksActivity.java [deleted file]
app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksActivity.kt [new file with mode: 0644]
app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksDatabaseViewActivity.java [deleted file]
app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksDatabaseViewActivity.kt [new file with mode: 0644]
app/src/main/java/com/stoutner/privacybrowser/activities/DomainsActivity.kt
app/src/main/java/com/stoutner/privacybrowser/activities/LogcatActivity.kt
app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java [deleted file]
app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.kt [new file with mode: 0644]
app/src/main/java/com/stoutner/privacybrowser/activities/RequestsActivity.java [deleted file]
app/src/main/java/com/stoutner/privacybrowser/activities/RequestsActivity.kt [new file with mode: 0644]
app/src/main/java/com/stoutner/privacybrowser/activities/ViewSourceActivity.kt
app/src/main/java/com/stoutner/privacybrowser/adapters/PinnedMismatchPagerAdapter.kt
app/src/main/java/com/stoutner/privacybrowser/coroutines/GetHostIpAddressesCoroutine.kt
app/src/main/java/com/stoutner/privacybrowser/coroutines/PrepareSaveDialogCoroutine.kt
app/src/main/java/com/stoutner/privacybrowser/dataclasses/HistoryDataClass.kt
app/src/main/java/com/stoutner/privacybrowser/dataclasses/PendingDialogDataClass.kt
app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateBookmarkDialog.kt
app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateBookmarkFolderDialog.kt
app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateHomeScreenShortcutDialog.kt
app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDatabaseViewDialog.kt
app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDialog.kt
app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDatabaseViewDialog.kt
app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDialog.kt
app/src/main/java/com/stoutner/privacybrowser/dialogs/FontSizeDialog.kt
app/src/main/java/com/stoutner/privacybrowser/dialogs/HttpAuthenticationDialog.kt
app/src/main/java/com/stoutner/privacybrowser/dialogs/MoveToFolderDialog.kt
app/src/main/java/com/stoutner/privacybrowser/dialogs/PinnedMismatchDialog.kt
app/src/main/java/com/stoutner/privacybrowser/dialogs/ProxyNotInstalledDialog.kt
app/src/main/java/com/stoutner/privacybrowser/dialogs/SaveDialog.kt
app/src/main/java/com/stoutner/privacybrowser/dialogs/SslCertificateErrorDialog.kt
app/src/main/java/com/stoutner/privacybrowser/dialogs/UrlHistoryDialog.kt
app/src/main/java/com/stoutner/privacybrowser/dialogs/ViewRequestDialog.kt
app/src/main/java/com/stoutner/privacybrowser/dialogs/ViewSslCertificateDialog.kt
app/src/main/java/com/stoutner/privacybrowser/fragments/AboutVersionFragment.kt
app/src/main/java/com/stoutner/privacybrowser/fragments/DomainSettingsFragment.kt
app/src/main/java/com/stoutner/privacybrowser/fragments/SettingsFragment.kt
app/src/main/java/com/stoutner/privacybrowser/fragments/WebViewTabFragment.kt
app/src/main/java/com/stoutner/privacybrowser/helpers/CheckPinnedMismatchHelper.kt
app/src/main/java/com/stoutner/privacybrowser/helpers/SanitizeUrlHelper.kt
app/src/main/java/com/stoutner/privacybrowser/helpers/UrlHelper.kt
app/src/main/java/com/stoutner/privacybrowser/views/NestedScrollWebView.kt
app/src/main/java/com/stoutner/privacybrowser/views/NoSwipeViewPager.kt
app/src/main/res/menu/bookmarks_context_menu.xml
app/src/main/res/menu/bookmarks_options_menu.xml
app/src/main/res/values-de/strings.xml
app/src/main/res/values-es/strings.xml
app/src/main/res/values-fr/strings.xml
app/src/main/res/values-it/strings.xml
app/src/main/res/values-night/colors.xml
app/src/main/res/values-pt-rBR/strings.xml
app/src/main/res/values-ru/strings.xml
app/src/main/res/values-tr/strings.xml
app/src/main/res/values-zh-rCN/strings.xml
app/src/main/res/values/colors.xml
app/src/main/res/values/strings.xml
fastlane/metadata/android/fr-FR/changelogs/67.txt [new file with mode: 0644]
fastlane/metadata/android/fr-FR/changelogs/68.txt [new file with mode: 0644]
fastlane/metadata/android/zh-CN/changelogs/68.txt

index 12287c6202fc96716092e064879ea994778d08c1..2f124b09a7301d84f3368c9eee0bea0db3f454bf 100644 (file)
         <h3><a href="https://www.stoutner.com/privacy-browser-android-3-13-4/">3.13.4</a> (version du code 68)</h3>
         <p><a href="https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=commitdiff;h=7ab6a9027175e6a8f72b3f3683f8c4e05643d8bc">27 Mars 2023</a> - API minimale : 24, API optimale : 33</p>
         <ul>
-            <li>Fix an <a href="https://redmine.stoutner.com/issues/981">intermittent crash</a> on restart caused by the
-                <a href="https://redmine.stoutner.com/issues/963">race condition fix</a> introduced in 3.13.3.</li>
-            <li>Add a <a href="https://redmine.stoutner.com/issues/962">monochrome icon</a> option.</li>
-            <li>Bump the <a href="https://redmine.stoutner.com/issues/957">minimum API to 24</a> (Android 7).</li>
+            <li>Correction d'un <a href="https://redmine.stoutner.com/issues/981">plantage intermittent</a> au redémarrage causé par
+                <a href="https://redmine.stoutner.com/issues/963">la correction d'un problème rare</a> introduit en version 3.13.3.</li>
+            <li>Ajout de l'option <a href="https://redmine.stoutner.com/issues/962">d'une icône monochrome</a>.</li>
+            <li>Passage de <a href="https://redmine.stoutner.com/issues/957">l'API minimale à 24</a> (Android 7).</li>
         </ul>
 
         <h3><a href="https://www.stoutner.com/privacy-browser-android-3-13-3/">3.13.3</a> (version du code 67)</h3>
         <p><a href="https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=commitdiff;h=c5359b173d559fc154bf7e4bd8fd65c951c529b3">13 Mars 2023</a> - API minimale : 23, API optimale : 33</p>
         <ul>
-            <li>Fix a race condition that sometimes caused the <a href="https://redmine.stoutner.com/issues/963">current tab to not be the active tab</a>.</li>
+            <li>Correction d'un problème rare qui faisait que <a href="https://redmine.stoutner.com/issues/963">l'onglet courant n'était pas toujours l'onglet actif</a>.</li>
         </ul>
 
         <h3><a href="https://www.stoutner.com/privacy-browser-android-3-13-2/">3.13.2</a> (version du code 66)</h3>
index 6b67b5b504997bf5e0cef5881815108b8c3c6912..ab948322ddacce3ef066cc4429cc65f825c6d51d 100644 (file)
@@ -32,7 +32,7 @@
         <h3><a href="https://www.stoutner.com/privacy-browser-android-3-13-4/">3.13.4</a> (version code 68)</h3>
         <p><a href="https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=commitdiff;h=7ab6a9027175e6a8f72b3f3683f8c4e05643d8bc">27 March 2023</a> - 最低支持API 24, 最高支持API 33</p>
         <ul>
-            <li>修复由3.13.3引入的<a href="https://redmine.stoutner.com/issues/981">race condition fix</a> 导致的<a href="https://redmine.stoutner.com/issues/963">频繁崩溃</a>。</li>
+            <li>修复由3.13.3引入的<a href="https://redmine.stoutner.com/issues/981">导致的</a><a href="https://redmine.stoutner.com/issues/963">频繁崩溃</a>。</li>
             <li>增加<a href="https://redmine.stoutner.com/issues/962">黑白图标</a>选项。</li>
             <li>增加<a href="https://redmine.stoutner.com/issues/957">最低支持API到24</a> (Android 7)。</li>
         </ul>
diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksActivity.java
deleted file mode 100644 (file)
index 8c14b0e..0000000
+++ /dev/null
@@ -1,1221 +0,0 @@
-/*
- * Copyright 2016-2023 Soren Stoutner <soren@stoutner.com>.
- *
- * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
- *
- * Privacy Browser Android 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 Android 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 Android.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.stoutner.privacybrowser.activities;
-
-import android.annotation.SuppressLint;
-import android.app.Dialog;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Typeface;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.util.SparseBooleanArray;
-import android.view.ActionMode;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.AbsListView;
-import android.widget.CursorAdapter;
-import android.widget.EditText;
-import android.widget.ImageView;
-import android.widget.ListView;
-import android.widget.RadioButton;
-import android.widget.TextView;
-
-import androidx.activity.OnBackPressedCallback;
-import androidx.annotation.NonNull;
-import androidx.appcompat.app.ActionBar;
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.appcompat.widget.Toolbar;
-import androidx.fragment.app.DialogFragment;
-import androidx.preference.PreferenceManager;
-
-import com.google.android.material.floatingactionbutton.FloatingActionButton;
-import com.google.android.material.snackbar.Snackbar;
-
-import com.stoutner.privacybrowser.R;
-import com.stoutner.privacybrowser.dialogs.CreateBookmarkDialog;
-import com.stoutner.privacybrowser.dialogs.CreateBookmarkFolderDialog;
-import com.stoutner.privacybrowser.dialogs.EditBookmarkDialog;
-import com.stoutner.privacybrowser.dialogs.EditBookmarkFolderDialog;
-import com.stoutner.privacybrowser.dialogs.MoveToFolderDialog;
-import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper;
-
-import java.io.ByteArrayOutputStream;
-import java.util.ArrayList;
-
-public class BookmarksActivity extends AppCompatActivity implements CreateBookmarkDialog.CreateBookmarkListener, CreateBookmarkFolderDialog.CreateBookmarkFolderListener, EditBookmarkDialog.EditBookmarkListener,
-        EditBookmarkFolderDialog.EditBookmarkFolderListener, MoveToFolderDialog.MoveToFolderListener {
-
-    // Declare the public static variables, which are accessed from the bookmarks database view activity.
-    public static String currentFolder;
-    public static boolean restartFromBookmarksDatabaseViewActivity;
-
-    // Define the saved instance state constants.
-    private final String CHECKED_BOOKMARKS_ARRAY_LIST = "checked_bookmarks_array_list";
-    private final String CURRENT_FOLDER = "current_folder";
-
-    // Define the class menu items.
-    private MenuItem moveBookmarkUpMenuItem;
-    private MenuItem moveBookmarkDownMenuItem;
-
-    // Declare the class variables.
-    private BookmarksDatabaseHelper bookmarksDatabaseHelper;
-    private Snackbar bookmarksDeletedSnackbar;
-    private boolean closeActivityAfterDismissingSnackbar;
-
-    // `bookmarksListView` is used in `onCreate()`, `onOptionsItemSelected()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveBookmark()`, `onSaveBookmarkFolder()`, `onMoveToFolder()`,
-    // `updateMoveIcons()`, `scrollBookmarks()`, and `loadFolder()`.
-    private ListView bookmarksListView;
-
-    // `bookmarksCursor` is used in `onCreate()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveBookmark()`, `onSaveBookmarkFolder()`, `onMoveToFolder()`, `deleteBookmarkFolderContents()`,
-    // `loadFolder()`, and `onDestroy()`.
-    private Cursor bookmarksCursor;
-
-    // `bookmarksCursorAdapter` is used in `onCreate(), `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveBookmark()`, `onSaveBookmarkFolder()`, `onMoveToFolder()`, and `onLoadFolder()`.
-    private CursorAdapter bookmarksCursorAdapter;
-
-    // `contextualActionMode` is used in `onCreate()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()` and `onMoveToFolder()`.
-    private ActionMode contextualActionMode;
-
-    // `appBar` is used in `onCreate()` and `loadFolder()`.
-    private ActionBar appBar;
-
-    // `oldFolderName` is used in `onCreate()` and `onSaveBookmarkFolder()`.
-    private String oldFolderNameString;
-
-    // The favorite icon byte array is populated in `onCreate()` and used in `onOptionsItemSelected()`.
-    private byte[] favoriteIconByteArray;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        // Get a handle for the shared preferences.
-        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
-
-        // Get the preferences.
-        boolean allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false);
-        boolean bottomAppBar = sharedPreferences.getBoolean(getString(R.string.bottom_app_bar_key), false);
-
-        // Disable screenshots if not allowed.
-        if (!allowScreenshots) {
-            getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
-        }
-
-        // Run the default commands.
-        super.onCreate(savedInstanceState);
-
-        // Get the intent that launched the activity.
-        Intent launchingIntent = getIntent();
-
-        // Store the current URL and title.
-        String currentUrl = launchingIntent.getStringExtra("current_url");
-        String currentTitle = launchingIntent.getStringExtra("current_title");
-
-        // Set the current folder variable.
-        if (launchingIntent.getStringExtra("current_folder") != null) {  // Set the current folder from the intent.
-            currentFolder = launchingIntent.getStringExtra("current_folder");
-        } else {  // Set the current folder to be `""`, which is the home folder.
-            currentFolder = "";
-        }
-
-        // Get the favorite icon byte array.
-        favoriteIconByteArray = launchingIntent.getByteArrayExtra("favorite_icon_byte_array");
-
-        // Remove the incorrect lint warning that the favorite icon byte array might be null.
-        assert favoriteIconByteArray != null;
-
-        // Convert the favorite icon byte array to a bitmap.
-        Bitmap favoriteIconBitmap = BitmapFactory.decodeByteArray(favoriteIconByteArray, 0, favoriteIconByteArray.length);
-
-        // Set the content according to the app bar position.
-        if (bottomAppBar) {
-            // Set the content view.
-            setContentView(R.layout.bookmarks_bottom_appbar);
-        } else {
-            // `Window.FEATURE_ACTION_MODE_OVERLAY` makes the contextual action mode cover the support action bar.  It must be requested before the content is set.
-            supportRequestWindowFeature(Window.FEATURE_ACTION_MODE_OVERLAY);
-
-            // Set the content view.
-            setContentView(R.layout.bookmarks_top_appbar);
-        }
-
-        // Get a handle for the toolbar.
-        final Toolbar toolbar = findViewById(R.id.bookmarks_toolbar);
-
-        // Set the support action bar.
-        setSupportActionBar(toolbar);
-
-        // Get handles for the views.
-        appBar = getSupportActionBar();
-        bookmarksListView = findViewById(R.id.bookmarks_listview);
-
-        // Remove the incorrect lint warning that `appBar` might be null.
-        assert appBar != null;
-
-        // Display the home arrow on the app bar.
-        appBar.setDisplayHomeAsUpEnabled(true);
-
-        // Control what the system back command does.
-        OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(true) {
-            @Override
-            public void handleOnBackPressed() {
-                // Prepare to finish the activity.
-                prepareFinish();
-            }
-        };
-
-        // Register the on back pressed callback.
-        getOnBackPressedDispatcher().addCallback(this, onBackPressedCallback);
-
-        // Initialize the database helper.
-        bookmarksDatabaseHelper = new BookmarksDatabaseHelper(this);
-
-        // Set a listener so that tapping a list item loads the URL or folder.
-        bookmarksListView.setOnItemClickListener((parent, view, position, id) -> {
-            // Convert the id from long to int to match the format of the bookmarks database.
-            int databaseId = (int) id;
-
-            // Get the bookmark cursor for this ID.
-            Cursor bookmarkCursor = bookmarksDatabaseHelper.getBookmark(databaseId);
-
-            // Move the cursor to the first entry.
-            bookmarkCursor.moveToFirst();
-
-            // Act upon the bookmark according to the type.
-            if (bookmarkCursor.getInt(bookmarkCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.IS_FOLDER)) == 1) {  // The selected bookmark is a folder.
-                // Update the current folder.
-                currentFolder = bookmarkCursor.getString(bookmarkCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_NAME));
-
-                // Load the new folder.
-                loadFolder();
-            } else {  // The selected bookmark is not a folder.
-                // Instantiate the edit bookmark dialog.
-                DialogFragment editBookmarkDialog = EditBookmarkDialog.bookmarkDatabaseId(databaseId, favoriteIconBitmap);
-
-                // Make it so.
-                editBookmarkDialog.show(getSupportFragmentManager(), getResources().getString(R.string.edit_bookmark));
-            }
-
-            // Close the cursor.
-            bookmarkCursor.close();
-        });
-
-        // Handle long presses on the list view.
-        bookmarksListView.setMultiChoiceModeListener(new AbsListView.MultiChoiceModeListener() {
-            // Instantiate the common variables.
-            MenuItem editBookmarkMenuItem;
-            MenuItem deleteBookmarksMenuItem;
-            MenuItem selectAllBookmarksMenuItem;
-            boolean deletingBookmarks;
-
-            @Override
-            public boolean onCreateActionMode(ActionMode mode, Menu menu) {
-                // Inflate the menu for the contextual app bar.
-                getMenuInflater().inflate(R.menu.bookmarks_context_menu, menu);
-
-                // Set the title.
-                if (currentFolder.isEmpty()) {  // Use `R.string.bookmarks` if in the home folder.
-                    mode.setTitle(R.string.bookmarks);
-                } else {  // Use the current folder name as the title.
-                    mode.setTitle(currentFolder);
-                }
-
-                // Get handles for menu items that need to be selectively disabled.
-                moveBookmarkUpMenuItem = menu.findItem(R.id.move_bookmark_up);
-                moveBookmarkDownMenuItem = menu.findItem(R.id.move_bookmark_down);
-                editBookmarkMenuItem = menu.findItem(R.id.edit_bookmark);
-                deleteBookmarksMenuItem = menu.findItem(R.id.delete_bookmark);
-                selectAllBookmarksMenuItem = menu.findItem(R.id.context_menu_select_all_bookmarks);
-
-                // Disable the delete bookmarks menu item if a delete is pending.
-                deleteBookmarksMenuItem.setEnabled(!deletingBookmarks);
-
-                // Store a handle for the contextual action mode so it can be closed programatically.
-                contextualActionMode = mode;
-
-                // Make it so.
-                return true;
-            }
-
-            @Override
-            public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
-                // Get a handle for the move to folder menu item.
-                MenuItem moveToFolderMenuItem = menu.findItem(R.id.move_to_folder);
-
-                // Get a Cursor with all of the folders.
-                Cursor folderCursor = bookmarksDatabaseHelper.getAllFolders();
-
-                // Enable the move to folder menu item if at least one folder exists.
-                moveToFolderMenuItem.setVisible(folderCursor.getCount() > 0);
-
-                // Make it so.
-                return true;
-            }
-
-            @Override
-            public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
-                // Get the number of selected bookmarks.
-                int numberOfSelectedBookmarks = bookmarksListView.getCheckedItemCount();
-
-                // Only process commands if at least one bookmark is selected.  Otherwise, a context menu with 0 selected bookmarks is briefly displayed.
-                if (numberOfSelectedBookmarks > 0) {
-                    // Adjust the ActionMode and the menu according to the number of selected bookmarks.
-                    if (numberOfSelectedBookmarks == 1) {  // One bookmark is selected.
-                        // List the number of selected bookmarks in the subtitle.
-                        mode.setSubtitle(getString(R.string.selected) + "1");
-
-                        // Show the `Move Up`, `Move Down`, and  `Edit` options.
-                        moveBookmarkUpMenuItem.setVisible(true);
-                        moveBookmarkDownMenuItem.setVisible(true);
-                        editBookmarkMenuItem.setVisible(true);
-
-                        // Update the enabled status of the move icons.
-                        updateMoveIcons();
-                    } else {  // More than one bookmark is selected.
-                        // List the number of selected bookmarks in the subtitle.
-                        mode.setSubtitle(getString(R.string.selected) + numberOfSelectedBookmarks);
-
-                        // Hide non-applicable `MenuItems`.
-                        moveBookmarkUpMenuItem.setVisible(false);
-                        moveBookmarkDownMenuItem.setVisible(false);
-                        editBookmarkMenuItem.setVisible(false);
-                    }
-
-                    // Show the select all menu item if all the bookmarks are not selected.
-                    selectAllBookmarksMenuItem.setVisible(bookmarksListView.getCheckedItemCount() != bookmarksListView.getCount());
-                }
-            }
-
-            @Override
-            public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
-                // Declare the common variables.
-                int selectedBookmarkNewPosition;
-                final SparseBooleanArray selectedBookmarksPositionsSparseBooleanArray;
-
-                // Initialize the selected bookmark position.
-                int selectedBookmarkPosition = 0;
-
-                // Get the menu item ID.
-                int menuItemId = menuItem.getItemId();
-
-                // Run the commands according to the selected action item.
-                if (menuItemId == R.id.move_bookmark_up) {  // Move the bookmark up.
-                    // Get the array of checked bookmark positions.
-                    selectedBookmarksPositionsSparseBooleanArray = bookmarksListView.getCheckedItemPositions();
-
-                    // Get the position of the bookmark that is selected.  If other bookmarks have previously been selected they will be included in the sparse boolean array with a value of `false`.
-                    for (int i = 0; i < selectedBookmarksPositionsSparseBooleanArray.size(); i++) {
-                        // Check to see if the value for the bookmark is true, meaning it is currently selected.
-                        if (selectedBookmarksPositionsSparseBooleanArray.valueAt(i)) {
-                            // Only one bookmark should have a value of `true` when move bookmark up is enabled.
-                            selectedBookmarkPosition = selectedBookmarksPositionsSparseBooleanArray.keyAt(i);
-                        }
-                    }
-
-                    // Calculate the new position of the selected bookmark.
-                    selectedBookmarkNewPosition = selectedBookmarkPosition - 1;
-
-                    // Iterate through the bookmarks.
-                    for (int i = 0; i < bookmarksListView.getCount(); i++) {
-                        // Get the database ID for the current bookmark.
-                        int currentBookmarkDatabaseId = (int) bookmarksListView.getItemIdAtPosition(i);
-
-                        // Update the display order for the current bookmark.
-                        if (i == selectedBookmarkPosition) {  // The current bookmark is the selected bookmark.
-                            // Move the current bookmark up one.
-                            bookmarksDatabaseHelper.updateDisplayOrder(currentBookmarkDatabaseId, i - 1);
-                        } else if ((i + 1) == selectedBookmarkPosition) {  // The current bookmark is immediately above the selected bookmark.
-                            // Move the current bookmark down one.
-                            bookmarksDatabaseHelper.updateDisplayOrder(currentBookmarkDatabaseId, i + 1);
-                        } else {  // The current bookmark is not changing positions.
-                            // 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.getColumnIndexOrThrow(BookmarksDatabaseHelper.DISPLAY_ORDER)) != i) {
-                                bookmarksDatabaseHelper.updateDisplayOrder(currentBookmarkDatabaseId, i);
-                            }
-                        }
-                    }
-
-                    // Update the bookmarks cursor with the current contents of the bookmarks database.
-                    bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolder);
-
-                    // Update the list view.
-                    bookmarksCursorAdapter.changeCursor(bookmarksCursor);
-
-                    // Scroll with the bookmark.
-                    scrollBookmarks(selectedBookmarkNewPosition);
-
-                    // Update the enabled status of the move icons.
-                    updateMoveIcons();
-                } else if (menuItemId == R.id.move_bookmark_down) {  // Move the bookmark down.
-                    // Get the array of checked bookmark positions.
-                    selectedBookmarksPositionsSparseBooleanArray = bookmarksListView.getCheckedItemPositions();
-
-                    // Get the position of the bookmark that is selected.  If other bookmarks have previously been selected they will be included in the sparse boolean array with a value of `false`.
-                    for (int i = 0; i < selectedBookmarksPositionsSparseBooleanArray.size(); i++) {
-                        // Check to see if the value for the bookmark is true, meaning it is currently selected.
-                        if (selectedBookmarksPositionsSparseBooleanArray.valueAt(i)) {
-                            // Only one bookmark should have a value of `true` when move bookmark down is enabled.
-                            selectedBookmarkPosition = selectedBookmarksPositionsSparseBooleanArray.keyAt(i);
-                        }
-                    }
-
-                    // Calculate the new position of the selected bookmark.
-                    selectedBookmarkNewPosition = selectedBookmarkPosition + 1;
-
-                    // Iterate through the bookmarks.
-                    for (int i = 0; i < bookmarksListView.getCount(); i++) {
-                        // Get the database ID for the current bookmark.
-                        int currentBookmarkDatabaseId = (int) bookmarksListView.getItemIdAtPosition(i);
-
-                        // Update the display order for the current bookmark.
-                        if (i == selectedBookmarkPosition) {  // The current bookmark is the selected bookmark.
-                            // Move the current bookmark down one.
-                            bookmarksDatabaseHelper.updateDisplayOrder(currentBookmarkDatabaseId, i + 1);
-                        } else if ((i - 1) == selectedBookmarkPosition) {  // The current bookmark is immediately below the selected bookmark.
-                            // Move the bookmark below the selected bookmark up one.
-                            bookmarksDatabaseHelper.updateDisplayOrder(currentBookmarkDatabaseId, i - 1);
-                        } else {  // The current bookmark is not changing positions.
-                            // 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.getColumnIndexOrThrow(BookmarksDatabaseHelper.DISPLAY_ORDER)) != i) {
-                                bookmarksDatabaseHelper.updateDisplayOrder(currentBookmarkDatabaseId, i);
-                            }
-                        }
-                    }
-
-                    // Update the bookmarks cursor with the current contents of the bookmarks database.
-                    bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolder);
-
-                    // Update the list view.
-                    bookmarksCursorAdapter.changeCursor(bookmarksCursor);
-
-                    // Scroll with the bookmark.
-                    scrollBookmarks(selectedBookmarkNewPosition);
-
-                    // Update the enabled status of the move icons.
-                    updateMoveIcons();
-                } else if (menuItemId == R.id.move_to_folder) {  // Move to folder.
-                    // Instantiate the move to folder alert dialog.
-                    DialogFragment moveToFolderDialog = MoveToFolderDialog.moveBookmarks(currentFolder, bookmarksListView.getCheckedItemIds());
-
-                    // Show the move to folder alert dialog.
-                    moveToFolderDialog.show(getSupportFragmentManager(), getResources().getString(R.string.move_to_folder));
-                } else if (menuItemId == R.id.edit_bookmark) {
-                    // Get the array of checked bookmark positions.
-                    selectedBookmarksPositionsSparseBooleanArray = bookmarksListView.getCheckedItemPositions();
-
-                    // Get the position of the bookmark that is selected.  If other bookmarks have previously been selected they will be included in the sparse boolean array with a value of `false`.
-                    for (int i = 0; i < selectedBookmarksPositionsSparseBooleanArray.size(); i++) {
-                        // Check to see if the value for the bookmark is true, meaning it is currently selected.
-                        if (selectedBookmarksPositionsSparseBooleanArray.valueAt(i)) {
-                            // Only one bookmark should have a value of `true` when move edit bookmark is enabled.
-                            selectedBookmarkPosition = selectedBookmarksPositionsSparseBooleanArray.keyAt(i);
-                        }
-                    }
-
-                    // Move the cursor to the selected position.
-                    bookmarksCursor.moveToPosition(selectedBookmarkPosition);
-
-                    // Find out if this bookmark is a folder.
-                    boolean isFolder = (bookmarksCursor.getInt(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.IS_FOLDER)) == 1);
-
-                    // Get the selected bookmark database ID.
-                    int databaseId = bookmarksCursor.getInt(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.ID));
-
-                    // Show the edit bookmark or edit bookmark folder dialog.
-                    if (isFolder) {
-                        // Save the current folder name, which is used in `onSaveBookmarkFolder()`.
-                        oldFolderNameString = bookmarksCursor.getString(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_NAME));
-
-                        // Instantiate the edit bookmark folder dialog.
-                        DialogFragment editFolderDialog = EditBookmarkFolderDialog.folderDatabaseId(databaseId, favoriteIconBitmap);
-
-                        // Make it so.
-                        editFolderDialog.show(getSupportFragmentManager(), getResources().getString(R.string.edit_folder));
-                    } else {
-                        // Instantiate the edit bookmark dialog.
-                        DialogFragment editBookmarkDialog = EditBookmarkDialog.bookmarkDatabaseId(databaseId, favoriteIconBitmap);
-
-                        // Make it so.
-                        editBookmarkDialog.show(getSupportFragmentManager(), getResources().getString(R.string.edit_bookmark));
-                    }
-                } else if (menuItemId == R.id.delete_bookmark) {  // Delete bookmark.
-                    // Set the deleting bookmarks flag, which prevents the delete menu item from being enabled until the current process finishes.
-                    deletingBookmarks = true;
-
-                    // Get an array of the selected row IDs.
-                    final long[] selectedBookmarksIdsLongArray = bookmarksListView.getCheckedItemIds();
-
-                    // Initialize a variable to count the number of bookmarks to delete.
-                    int numberOfBookmarksToDelete = 0;
-
-                    // Count the number of bookmarks.
-                    for (long databaseIdLong : selectedBookmarksIdsLongArray) {
-                        // Convert the database ID long to an int.
-                        int databaseIdInt = (int) databaseIdLong;
-
-                        // Count the contents of the folder if the selected bookmark is a folder.
-                        if (bookmarksDatabaseHelper.isFolder(databaseIdInt)) {
-                            // Add the bookmarks from the folder to the running total.
-                            numberOfBookmarksToDelete = numberOfBookmarksToDelete + countBookmarkFolderContents(databaseIdInt);
-                        }
-
-                        // Increment the count of the number of bookmarks to delete.
-                        numberOfBookmarksToDelete++;
-                    }
-
-                    // Get an array of checked bookmarks.  `.clone()` makes a copy that won't change if the list view is reloaded, which is needed for re-selecting the bookmarks on undelete.
-                    selectedBookmarksPositionsSparseBooleanArray = bookmarksListView.getCheckedItemPositions().clone();
-
-                    // Update the bookmarks cursor with the current contents of the bookmarks database except for the specified database IDs.
-                    bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrderExcept(selectedBookmarksIdsLongArray, currentFolder);
-
-                    // Update the list view.
-                    bookmarksCursorAdapter.changeCursor(bookmarksCursor);
-
-                    // Create a Snackbar with the number of deleted bookmarks.
-                    bookmarksDeletedSnackbar = Snackbar.make(findViewById(R.id.bookmarks_coordinatorlayout), getString(R.string.bookmarks_deleted) + numberOfBookmarksToDelete,
-                            Snackbar.LENGTH_LONG)
-                            .setAction(R.string.undo, view -> {
-                                // Do nothing because everything will be handled by `onDismissed()` below.
-                            })
-                            .addCallback(new Snackbar.Callback() {
-                                @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) {
-                                    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);
-                                            }
-
-                                            // 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);
-
-                                            // Update the display order only if it is not correct in the database.
-                                            if (bookmarksCursor.getInt(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.DISPLAY_ORDER)) != i) {
-                                                bookmarksDatabaseHelper.updateDisplayOrder(currentBookmarkDatabaseId, i);
-                                            }
-                                        }
-                                    }
-
-                                    // Reset the deleting bookmarks flag.
-                                    deletingBookmarks = false;
-
-                                    // Enable the delete bookmarks menu item.
-                                    deleteBookmarksMenuItem.setEnabled(true);
-
-                                    // Close the activity if back has been pressed.
-                                    if (closeActivityAfterDismissingSnackbar) {
-                                        finish();
-                                    }
-                                }
-                            });
-
-                    //Show the Snackbar.
-                    bookmarksDeletedSnackbar.show();
-                } else if (menuItemId == R.id.context_menu_select_all_bookmarks) {  // Select all.
-                    // Get the total number of bookmarks.
-                    int numberOfBookmarks = bookmarksListView.getCount();
-
-                    // Select them all.
-                    for (int i = 0; i < numberOfBookmarks; i++) {
-                        bookmarksListView.setItemChecked(i, true);
-                    }
-                }
-
-                // Consume the click.
-                return true;
-            }
-
-            @Override
-            public void onDestroyActionMode(ActionMode mode) {
-                // Do nothing.
-            }
-        });
-
-        // Get handles for the floating action buttons.
-        FloatingActionButton createBookmarkFolderFab = findViewById(R.id.create_bookmark_folder_fab);
-        FloatingActionButton createBookmarkFab = findViewById(R.id.create_bookmark_fab);
-
-        // Set the create new bookmark folder FAB to display the `AlertDialog`.
-        createBookmarkFolderFab.setOnClickListener(v -> {
-            // Create a create bookmark folder dialog.
-            DialogFragment createBookmarkFolderDialog = CreateBookmarkFolderDialog.createBookmarkFolder(favoriteIconBitmap);
-
-            // Show the create bookmark folder dialog.
-            createBookmarkFolderDialog.show(getSupportFragmentManager(), getString(R.string.create_folder));
-        });
-
-        // Set the create new bookmark FAB to display the alert dialog.
-        createBookmarkFab.setOnClickListener(view -> {
-            // Remove the incorrect lint warning below.
-            assert currentUrl != null;
-            assert currentTitle != null;
-
-            // Instantiate the create bookmark dialog.
-            DialogFragment createBookmarkDialog = CreateBookmarkDialog.createBookmark(currentUrl, currentTitle, favoriteIconBitmap);
-
-            // Display the create bookmark dialog.
-            createBookmarkDialog.show(getSupportFragmentManager(), getResources().getString(R.string.create_bookmark));
-        });
-
-        // Restore the state if the app has been restarted.
-        if (savedInstanceState != null) {
-            // Restore the current folder.
-            currentFolder = savedInstanceState.getString(CURRENT_FOLDER);
-
-            // Update the bookmarks list view after it has loaded.
-            bookmarksListView.post(() -> {
-                // Get the checked bookmarks array list.
-                ArrayList<Integer> checkedBookmarksArrayList = savedInstanceState.getIntegerArrayList(CHECKED_BOOKMARKS_ARRAY_LIST);
-
-                // Check each previously checked bookmark in the list view.
-                if (checkedBookmarksArrayList != null)
-                    checkedBookmarksArrayList.forEach((position) -> bookmarksListView.setItemChecked(position, true));
-            });
-        }
-
-        // Load the home folder.
-        loadFolder();
-    }
-
-    @Override
-    public void onRestart() {
-        // Run the default commands.
-        super.onRestart();
-
-        // Update the list view if returning from the bookmarks database view activity.
-        if (restartFromBookmarksDatabaseViewActivity) {
-            // Load the current folder in the list view.
-            loadFolder();
-
-            // Reset `restartFromBookmarksDatabaseViewActivity`.
-            restartFromBookmarksDatabaseViewActivity = false;
-        }
-    }
-
-    @Override
-    public void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
-        // Run the default commands.
-        super.onSaveInstanceState(savedInstanceState);
-
-        // Get the array of the checked items.
-        SparseBooleanArray checkedBookmarksSparseBooleanArray = bookmarksListView.getCheckedItemPositions();
-
-        // Create a checked items array list.
-        ArrayList<Integer> checkedBookmarksArrayList = new ArrayList<>();
-
-        // Add each checked bookmark position to the array list.
-        for (int i = 0; i < checkedBookmarksSparseBooleanArray.size(); i++) {
-            // Check to see if the bookmark is currently checked.  Bookmarks that have previously been checked but currently aren't will be populated in the sparse boolean array, but will return false.
-            if (checkedBookmarksSparseBooleanArray.valueAt(i)) {
-                // Add the bookmark position to the checked bookmarks array list.
-                checkedBookmarksArrayList.add(checkedBookmarksSparseBooleanArray.keyAt(i));
-            }
-        }
-
-        // Store the variables in the saved instance state.
-        savedInstanceState.putString(CURRENT_FOLDER, currentFolder);
-        savedInstanceState.putIntegerArrayList(CHECKED_BOOKMARKS_ARRAY_LIST, checkedBookmarksArrayList);
-    }
-
-    @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
-        // Inflate the menu.
-        getMenuInflater().inflate(R.menu.bookmarks_options_menu, menu);
-
-        // Success.
-        return true;
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem menuItem) {
-        // Get a handle for the menu item ID.
-        int menuItemId = menuItem.getItemId();
-
-        // Run the command according to the selected option.
-        if (menuItemId == android.R.id.home) {  // Home.  The home arrow is identified as `android.R.id.home`, not just `R.id.home`.
-            if (currentFolder.isEmpty()) {  // Currently in the home folder.
-                // Prepare to finish the activity.
-                prepareFinish();
-            } else {  // Currently in a subfolder.
-                // Place the former parent folder in `currentFolder`.
-                currentFolder = bookmarksDatabaseHelper.getParentFolderName(currentFolder);
-
-                // Load the new folder.
-                loadFolder();
-            }
-        } else if (menuItemId == R.id.options_menu_select_all_bookmarks) {  // Select all.
-            // Get the total number of bookmarks.
-            int numberOfBookmarks = bookmarksListView.getCount();
-
-            // Select them all.
-            for (int i = 0; i < numberOfBookmarks; i++) {
-                bookmarksListView.setItemChecked(i, true);
-            }
-        } else if (menuItemId == R.id.bookmarks_database_view) {
-            // Close the contextual action bar if it is displayed.  This can happen if the bottom app bar is enabled.
-            if (contextualActionMode != null) {
-                contextualActionMode.finish();
-            }
-
-            // Create an intent to launch the bookmarks database view activity.
-            Intent bookmarksDatabaseViewIntent = new Intent(this, BookmarksDatabaseViewActivity.class);
-
-            // Include the favorite icon byte array to the intent.
-            bookmarksDatabaseViewIntent.putExtra("favorite_icon_byte_array", favoriteIconByteArray);
-
-            // Make it so.
-            startActivity(bookmarksDatabaseViewIntent);
-        }
-        return true;
-    }
-
-    @Override
-    public void onCreateBookmark(DialogFragment dialogFragment, Bitmap favoriteIconBitmap) {
-        // Get the alert dialog from the fragment.
-        Dialog dialog = dialogFragment.getDialog();
-
-        // Remove the incorrect lint warning below that the dialog might be null.
-        assert dialog != null;
-
-        // Get the views from the dialog fragment.
-        EditText createBookmarkNameEditText = dialog.findViewById(R.id.create_bookmark_name_edittext);
-        EditText createBookmarkUrlEditText = dialog.findViewById(R.id.create_bookmark_url_edittext);
-
-        // Extract the strings from the edit texts.
-        String bookmarkNameString = createBookmarkNameEditText.getText().toString();
-        String bookmarkUrlString = createBookmarkUrlEditText.getText().toString();
-
-        // Create a favorite icon byte array output stream.
-        ByteArrayOutputStream favoriteIconByteArrayOutputStream = new ByteArrayOutputStream();
-
-        // Convert the favorite icon bitmap to a byte array.  `0` is for lossless compression (the only option for a PNG).
-        favoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, favoriteIconByteArrayOutputStream);
-
-        // Convert the favorite icon byte array stream to a byte array.
-        byte[] favoriteIconByteArray = favoriteIconByteArrayOutputStream.toByteArray();
-
-        // Display the new bookmark below the current items in the (0 indexed) list.
-        int newBookmarkDisplayOrder = bookmarksListView.getCount();
-
-        // Create the bookmark.
-        bookmarksDatabaseHelper.createBookmark(bookmarkNameString, bookmarkUrlString, currentFolder, newBookmarkDisplayOrder, favoriteIconByteArray);
-
-        // Update the bookmarks cursor with the current contents of this folder.
-        bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolder);
-
-        // Update the `ListView`.
-        bookmarksCursorAdapter.changeCursor(bookmarksCursor);
-
-        // Scroll to the new bookmark.
-        bookmarksListView.setSelection(newBookmarkDisplayOrder);
-    }
-
-    @Override
-    public void onCreateBookmarkFolder(DialogFragment dialogFragment, @NonNull Bitmap favoriteIconBitmap) {
-        // Get the dialog from the dialog fragment.
-        Dialog dialog = dialogFragment.getDialog();
-
-        // Remove the incorrect lint warning below that the dialog might be null.
-        assert dialog != null;
-
-        // Get handles for the views in the dialog fragment.
-        EditText folderNameEditText = dialog.findViewById(R.id.folder_name_edittext);
-        RadioButton defaultIconRadioButton = dialog.findViewById(R.id.default_icon_radiobutton);
-        ImageView defaultIconImageView = dialog.findViewById(R.id.default_icon_imageview);
-
-        // Get new folder name string.
-        String folderNameString = folderNameEditText.getText().toString();
-
-        // Create a folder icon bitmap.
-        Bitmap folderIconBitmap;
-
-        // Set the folder icon bitmap according to the dialog.
-        if (defaultIconRadioButton.isChecked()) {  // Use the default folder icon.
-            // Get the default folder icon drawable.
-            Drawable folderIconDrawable = defaultIconImageView.getDrawable();
-
-            // Convert the folder icon drawable to a bitmap drawable.
-            BitmapDrawable folderIconBitmapDrawable = (BitmapDrawable) folderIconDrawable;
-
-            // Convert the folder icon bitmap drawable to a bitmap.
-            folderIconBitmap = folderIconBitmapDrawable.getBitmap();
-        } else {  // Use the WebView favorite icon.
-            // Copy the favorite icon bitmap to the folder icon bitmap.
-            folderIconBitmap = favoriteIconBitmap;
-        }
-
-        // Create a folder icon byte array output stream.
-        ByteArrayOutputStream folderIconByteArrayOutputStream = new ByteArrayOutputStream();
-
-        // Convert the folder icon bitmap to a byte array.  `0` is for lossless compression (the only option for a PNG).
-        folderIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, folderIconByteArrayOutputStream);
-
-        // Convert the folder icon byte array stream to a byte array.
-        byte[] folderIconByteArray = folderIconByteArrayOutputStream.toByteArray();
-
-        // Move all the bookmarks down one in the display order.
-        for (int i = 0; i < bookmarksListView.getCount(); i++) {
-            int databaseId = (int) bookmarksListView.getItemIdAtPosition(i);
-            bookmarksDatabaseHelper.updateDisplayOrder(databaseId, i + 1);
-        }
-
-        // Create the folder, which will be placed at the top of the `ListView`.
-        bookmarksDatabaseHelper.createFolder(folderNameString, currentFolder, folderIconByteArray);
-
-        // Update the bookmarks cursor with the current contents of this folder.
-        bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolder);
-
-        // Update the list view.
-        bookmarksCursorAdapter.changeCursor(bookmarksCursor);
-
-        // Scroll to the new folder.
-        bookmarksListView.setSelection(0);
-    }
-
-    @Override
-    public void onSaveBookmark(DialogFragment dialogFragment, int selectedBookmarkDatabaseId, @NonNull Bitmap favoriteIconBitmap) {
-        // Get the dialog from the dialog fragment.
-        Dialog dialog = dialogFragment.getDialog();
-
-        // Remove the incorrect lint warning below that the dialog might be null.
-        assert dialog != null;
-
-        // Get handles for the views from the dialog fragment.
-        EditText bookmarkNameEditText = dialog.findViewById(R.id.bookmark_name_edittext);
-        EditText bookmarkUrlEditText = dialog.findViewById(R.id.bookmark_url_edittext);
-        RadioButton currentIconRadioButton = dialog.findViewById(R.id.current_icon_radiobutton);
-
-        // Store the bookmark strings.
-        String bookmarkNameString = bookmarkNameEditText.getText().toString();
-        String bookmarkUrlString = bookmarkUrlEditText.getText().toString();
-
-        // Update the bookmark.
-        if (currentIconRadioButton.isChecked()) {  // Update the bookmark without changing the favorite icon.
-            bookmarksDatabaseHelper.updateBookmark(selectedBookmarkDatabaseId, bookmarkNameString, bookmarkUrlString);
-        } else {  // Update the bookmark using the WebView favorite icon.
-            // Create a favorite icon byte array output stream.
-            ByteArrayOutputStream newFavoriteIconByteArrayOutputStream = new ByteArrayOutputStream();
-
-            // Convert the favorite icon bitmap to a byte array.  `0` is for lossless compression (the only option for a PNG).
-            favoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, newFavoriteIconByteArrayOutputStream);
-
-            // Convert the favorite icon byte array stream to a byte array.
-            byte[] newFavoriteIconByteArray = newFavoriteIconByteArrayOutputStream.toByteArray();
-
-            //  Update the bookmark and the favorite icon.
-            bookmarksDatabaseHelper.updateBookmark(selectedBookmarkDatabaseId, bookmarkNameString, bookmarkUrlString, newFavoriteIconByteArray);
-        }
-
-        // Check to see if the contextual action mode has been created.
-        if (contextualActionMode != null) {
-            // Close the contextual action mode if it is open.
-            contextualActionMode.finish();
-        }
-
-        // Update the bookmarks cursor with the contents of the current folder.
-        bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolder);
-
-        // Update the `ListView`.
-        bookmarksCursorAdapter.changeCursor(bookmarksCursor);
-    }
-
-    @Override
-    public void onSaveBookmarkFolder(DialogFragment dialogFragment, int selectedFolderDatabaseId, @NonNull Bitmap favoriteIconBitmap) {
-        // Get the dialog from the dialog fragment.
-        Dialog dialog = dialogFragment.getDialog();
-
-        // Remove the incorrect lint warning below that the dialog might be null.
-        assert dialog != null;
-
-        // Get handles for the views from the dialog fragment.
-        RadioButton currentFolderIconRadioButton = dialog.findViewById(R.id.current_icon_radiobutton);
-        RadioButton defaultFolderIconRadioButton = dialog.findViewById(R.id.default_icon_radiobutton);
-        ImageView defaultFolderIconImageView = dialog.findViewById(R.id.default_icon_imageview);
-        EditText editFolderNameEditText = dialog.findViewById(R.id.folder_name_edittext);
-
-        // Get the new folder name.
-        String newFolderNameString = editFolderNameEditText.getText().toString();
-
-        // Check if the favorite icon has changed.
-        if (currentFolderIconRadioButton.isChecked()) {  // Only the name has changed.
-            // Update the name in the database.
-            bookmarksDatabaseHelper.updateFolder(selectedFolderDatabaseId, oldFolderNameString, newFolderNameString);
-        } else if (!currentFolderIconRadioButton.isChecked() && newFolderNameString.equals(oldFolderNameString)) {  // Only the icon has changed.
-            // Create the new folder icon Bitmap.
-            Bitmap folderIconBitmap;
-
-            // Populate the new folder icon bitmap.
-            if (defaultFolderIconRadioButton.isChecked()) {
-                // Get the default folder icon drawable.
-                Drawable folderIconDrawable = defaultFolderIconImageView.getDrawable();
-
-                // Convert the folder icon drawable to a bitmap drawable.
-                BitmapDrawable folderIconBitmapDrawable = (BitmapDrawable) folderIconDrawable;
-
-                // Convert the folder icon bitmap drawable to a bitmap.
-                folderIconBitmap = folderIconBitmapDrawable.getBitmap();
-            } else {  // Use the WebView favorite icon.
-                // Copy the favorite icon bitmap to the folder icon bitmap.
-                folderIconBitmap = favoriteIconBitmap;
-            }
-
-            // Create a folder icon byte array output stream.
-            ByteArrayOutputStream newFolderIconByteArrayOutputStream = new ByteArrayOutputStream();
-
-            // Convert the folder icon bitmap to a byte array.  `0` is for lossless compression (the only option for a PNG).
-            folderIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, newFolderIconByteArrayOutputStream);
-
-            // Convert the folder icon byte array stream to a byte array.
-            byte[] newFolderIconByteArray = newFolderIconByteArrayOutputStream.toByteArray();
-
-            // Update the folder icon in the database.
-            bookmarksDatabaseHelper.updateFolder(selectedFolderDatabaseId, newFolderIconByteArray);
-        } else {  // The folder icon and the name have changed.
-            // Instantiate the new folder icon `Bitmap`.
-            Bitmap folderIconBitmap;
-
-            // Populate the new folder icon bitmap.
-            if (defaultFolderIconRadioButton.isChecked()) {
-                // Get the default folder icon drawable.
-                Drawable folderIconDrawable = defaultFolderIconImageView.getDrawable();
-
-                // Convert the folder icon drawable to a bitmap drawable.
-                BitmapDrawable folderIconBitmapDrawable = (BitmapDrawable) folderIconDrawable;
-
-                // Convert the folder icon bitmap drawable to a bitmap.
-                folderIconBitmap = folderIconBitmapDrawable.getBitmap();
-            } else {  // Use the WebView favorite icon.
-                // Copy the favorite icon bitmap to the folder icon bitmap.
-                folderIconBitmap = favoriteIconBitmap;
-            }
-
-            // Create a folder icon byte array output stream.
-            ByteArrayOutputStream newFolderIconByteArrayOutputStream = new ByteArrayOutputStream();
-
-            // Convert the folder icon bitmap to a byte array.  `0` is for lossless compression (the only option for a PNG).
-            folderIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, newFolderIconByteArrayOutputStream);
-
-            // Convert the folder icon byte array stream to a byte array.
-            byte[] newFolderIconByteArray = newFolderIconByteArrayOutputStream.toByteArray();
-
-            // Update the folder name and icon in the database.
-            bookmarksDatabaseHelper.updateFolder(selectedFolderDatabaseId, oldFolderNameString, newFolderNameString, newFolderIconByteArray);
-        }
-
-        // Update the bookmarks cursor with the current contents of this folder.
-        bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolder);
-
-        // Update the `ListView`.
-        bookmarksCursorAdapter.changeCursor(bookmarksCursor);
-
-        // Close the contextual action mode.
-        contextualActionMode.finish();
-    }
-
-    @Override
-    public void onMoveToFolder(DialogFragment dialogFragment) {
-        // Get the dialog from the dialog fragment.
-        Dialog dialog = dialogFragment.getDialog();
-
-        // Remove the incorrect lint warning below that the dialog might be null.
-        assert dialog != null;
-
-        // Get a handle for the list view from the dialog.
-        ListView folderListView = dialog.findViewById(R.id.move_to_folder_listview);
-
-        // Store a long array of the selected folders.
-        long[] newFolderLongArray = folderListView.getCheckedItemIds();
-
-        // Get the new folder database ID.  Only one folder will be selected.
-        int newFolderDatabaseId = (int) newFolderLongArray[0];
-
-        // Instantiate `newFolderName`.
-        String newFolderName;
-
-        // Set the new folder name.
-        if (newFolderDatabaseId == 0) {
-            // The new folder is the home folder, represented as `""` in the database.
-            newFolderName = "";
-        } else {
-            // Get the new folder name from the database.
-            newFolderName = bookmarksDatabaseHelper.getFolderName(newFolderDatabaseId);
-        }
-
-        // Get a long array with the the database ID of the selected bookmarks.
-        long[] selectedBookmarksLongArray = bookmarksListView.getCheckedItemIds();
-
-        // Move each of the selected bookmarks to the new folder.
-        for (long databaseIdLong : selectedBookmarksLongArray) {
-            // Get `databaseIdInt` for each selected bookmark.
-            int databaseIdInt = (int) databaseIdLong;
-
-            // Move the selected bookmark to the new folder.
-            bookmarksDatabaseHelper.moveToFolder(databaseIdInt, newFolderName);
-        }
-
-        // Update the bookmarks cursor with the current contents of this folder.
-        bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolder);
-
-        // Update the `ListView`.
-        bookmarksCursorAdapter.changeCursor(bookmarksCursor);
-
-        // Close the contextual app bar.
-        contextualActionMode.finish();
-    }
-
-    private int countBookmarkFolderContents(int databaseId) {
-        // Initialize the bookmark counter.
-        int bookmarkCounter = 0;
-
-        // Get the name of the folder.
-        String folderName = bookmarksDatabaseHelper.getFolderName(databaseId);
-
-        // Get the contents of the folder.
-        Cursor folderCursor = bookmarksDatabaseHelper.getBookmarkIds(folderName);
-
-        // Count each of the bookmarks in the folder.
-        for (int i = 0; i < folderCursor.getCount(); i++) {
-            // Move the folder cursor to the current row.
-            folderCursor.moveToPosition(i);
-
-            // Get the database ID of the item.
-            int itemDatabaseId = folderCursor.getInt(folderCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.ID));
-
-            // If this is a folder, recursively count the contents first.
-            if (bookmarksDatabaseHelper.isFolder(itemDatabaseId)) {
-                // Add the bookmarks from the folder to the running total.
-                bookmarkCounter = bookmarkCounter + countBookmarkFolderContents(itemDatabaseId);
-            }
-
-            // Add the bookmark to the running total.
-            bookmarkCounter++;
-        }
-
-        // Return the bookmark counter.
-        return bookmarkCounter;
-    }
-
-    private void deleteBookmarkFolderContents(int databaseId) {
-        // Get the name of the folder.
-        String folderName = bookmarksDatabaseHelper.getFolderName(databaseId);
-
-        // Get the contents of the folder.
-        Cursor folderCursor = bookmarksDatabaseHelper.getBookmarkIds(folderName);
-
-        // Delete each of the bookmarks in the folder.
-        for (int i = 0; i < folderCursor.getCount(); i++) {
-            // Move the folder cursor to the current row.
-            folderCursor.moveToPosition(i);
-
-            // Get the database ID of the item.
-            int itemDatabaseId = folderCursor.getInt(folderCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.ID));
-
-            // If this is a folder, recursively delete the contents first.
-            if (bookmarksDatabaseHelper.isFolder(itemDatabaseId)) {
-                deleteBookmarkFolderContents(itemDatabaseId);
-            }
-
-            // Delete the bookmark.
-            bookmarksDatabaseHelper.deleteBookmark(itemDatabaseId);
-        }
-    }
-
-    private void prepareFinish() {
-        // Check to see if a snackbar is currently displayed.  If so, it must be closed before exiting so that a pending delete is completed before reloading the list view in the bookmarks drawer.
-        if ((bookmarksDeletedSnackbar != null) && bookmarksDeletedSnackbar.isShown()) {  // Close the bookmarks deleted snackbar before going home.
-            // Set the close flag.
-            closeActivityAfterDismissingSnackbar = true;
-
-            // Dismiss the snackbar.
-            bookmarksDeletedSnackbar.dismiss();
-        } else {  // Go home immediately.
-            // Update the bookmarks folder for the bookmarks drawer in the main WebView activity.
-            MainWebViewActivity.currentBookmarksFolder = currentFolder;
-
-            // Close the bookmarks drawer and reload the bookmarks ListView when returning to the main WebView activity.
-            MainWebViewActivity.restartFromBookmarksActivity = true;
-
-            // Exit the bookmarks activity.
-            finish();
-        }
-    }
-
-    private void updateMoveIcons() {
-        // Get a long array of the selected bookmarks.
-        long[] selectedBookmarksLongArray = bookmarksListView.getCheckedItemIds();
-
-        // Get the database IDs for the first, last, and selected bookmarks.
-        int selectedBookmarkDatabaseId = (int) selectedBookmarksLongArray[0];
-        int firstBookmarkDatabaseId = (int) bookmarksListView.getItemIdAtPosition(0);
-        // bookmarksListView is 0 indexed.
-        int lastBookmarkDatabaseId = (int) bookmarksListView.getItemIdAtPosition(bookmarksListView.getCount() - 1);
-
-        // Update the move bookmark up menu item.
-        if (selectedBookmarkDatabaseId == firstBookmarkDatabaseId) {  // The selected bookmark is in the first position.
-            // Disable the move bookmark up menu item.
-            moveBookmarkUpMenuItem.setEnabled(false);
-
-            //  Set the icon.
-            moveBookmarkUpMenuItem.setIcon(R.drawable.move_up_disabled);
-        } else {  // The selected bookmark is not in the first position.
-            // Enable the move bookmark up menu item.
-            moveBookmarkUpMenuItem.setEnabled(true);
-
-            // Set the icon according to the theme.
-            moveBookmarkUpMenuItem.setIcon(R.drawable.move_up_enabled);
-        }
-
-        // Update the move bookmark down menu item.
-        if (selectedBookmarkDatabaseId == lastBookmarkDatabaseId) {  // The selected bookmark is in the last position.
-            // Disable the move bookmark down menu item.
-            moveBookmarkDownMenuItem.setEnabled(false);
-
-            // Set the icon.
-            moveBookmarkDownMenuItem.setIcon(R.drawable.move_down_disabled);
-        } else {  // The selected bookmark is not in the last position.
-            // Enable the move bookmark down menu item.
-            moveBookmarkDownMenuItem.setEnabled(true);
-
-            // Set the icon.
-            moveBookmarkDownMenuItem.setIcon(R.drawable.move_down_enabled);
-        }
-    }
-
-    private void scrollBookmarks(int selectedBookmarkPosition) {
-        // Get the first and last visible bookmark positions.
-        int firstVisibleBookmarkPosition = bookmarksListView.getFirstVisiblePosition();
-        int lastVisibleBookmarkPosition = bookmarksListView.getLastVisiblePosition();
-
-        // Calculate the number of bookmarks per screen.
-        int numberOfBookmarksPerScreen = lastVisibleBookmarkPosition - firstVisibleBookmarkPosition;
-
-        // Scroll with the moved bookmark if necessary.
-        if (selectedBookmarkPosition <= firstVisibleBookmarkPosition) {  // The selected bookmark position is at or above the top of the screen.
-            // Scroll to the selected bookmark position.
-            bookmarksListView.setSelection(selectedBookmarkPosition);
-        } else if (selectedBookmarkPosition >= (lastVisibleBookmarkPosition - 1)) {  // The selected bookmark is at or below the bottom of the screen.
-            // The `-1` handles partial bookmarks displayed at the bottom of the list view.  This command scrolls to display the selected bookmark at the bottom of the screen.
-            // `+1` assures that the entire bookmark will be displayed in situations where only a partial bookmark fits at the bottom of the list view.
-            bookmarksListView.setSelection(selectedBookmarkPosition - numberOfBookmarksPerScreen + 1);
-        }
-    }
-
-    private void loadFolder() {
-        // Update bookmarks cursor with the contents of the bookmarks database for the current folder.
-        bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolder);
-
-        // Setup a `CursorAdapter`.  `this` specifies the `Context`.  `false` disables `autoRequery`.
-        bookmarksCursorAdapter = new CursorAdapter(this, bookmarksCursor, false) {
-            @Override
-            public View newView(Context context, Cursor cursor, ViewGroup parent) {
-                // Inflate the individual item layout.  `false` does not attach it to the root.
-                return getLayoutInflater().inflate(R.layout.bookmarks_activity_item_linearlayout, parent, false);
-            }
-
-            @Override
-            public void bindView(View view, Context context, Cursor cursor) {
-                // Get handles for the views.
-                ImageView bookmarkFavoriteIcon = view.findViewById(R.id.bookmark_favorite_icon);
-                TextView bookmarkNameTextView = view.findViewById(R.id.bookmark_name);
-
-                // Get the favorite icon byte array from the `Cursor`.
-                byte[] favoriteIconByteArray = cursor.getBlob(cursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.FAVORITE_ICON));
-
-                // Convert the byte array to a `Bitmap` beginning at the first byte and ending at the last.
-                Bitmap favoriteIconBitmap = BitmapFactory.decodeByteArray(favoriteIconByteArray, 0, favoriteIconByteArray.length);
-
-                // Display the bitmap in `bookmarkFavoriteIcon`.
-                bookmarkFavoriteIcon.setImageBitmap(favoriteIconBitmap);
-
-                // Get the bookmark name from the cursor and display it in `bookmarkNameTextView`.
-                String bookmarkNameString = cursor.getString(cursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_NAME));
-                bookmarkNameTextView.setText(bookmarkNameString);
-
-                // Make the font bold for folders.
-                if (cursor.getInt(cursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.IS_FOLDER)) == 1) {
-                    bookmarkNameTextView.setTypeface(Typeface.DEFAULT_BOLD);
-                } else {  // Reset the font to default for normal bookmarks.
-                    bookmarkNameTextView.setTypeface(Typeface.DEFAULT);
-                }
-            }
-        };
-
-        // Populate the list view with the adapter.
-        bookmarksListView.setAdapter(bookmarksCursorAdapter);
-
-        // Set the `AppBar` title.
-        if (currentFolder.isEmpty()) {
-            appBar.setTitle(R.string.bookmarks);
-        } else {
-            appBar.setTitle(currentFolder);
-        }
-    }
-
-    @Override
-    public void onDestroy() {
-        // Close the bookmarks cursor and database.
-        bookmarksCursor.close();
-        bookmarksDatabaseHelper.close();
-
-        // Run the default commands.
-        super.onDestroy();
-    }
-}
diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksActivity.kt b/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksActivity.kt
new file mode 100644 (file)
index 0000000..43350b1
--- /dev/null
@@ -0,0 +1,1108 @@
+/*
+ * Copyright 2016-2023 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
+ *
+ * Privacy Browser Android 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 Android 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 Android.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.stoutner.privacybrowser.activities
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.content.Intent
+import android.database.Cursor
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.graphics.Typeface
+import android.graphics.drawable.BitmapDrawable
+import android.os.Bundle
+import android.util.SparseBooleanArray
+import android.view.ActionMode
+import android.view.Menu
+import android.view.MenuItem
+import android.view.View
+import android.view.ViewGroup
+import android.view.Window
+import android.view.WindowManager
+import android.widget.AbsListView.MultiChoiceModeListener
+import android.widget.AdapterView
+import android.widget.EditText
+import android.widget.ImageView
+import android.widget.ListView
+import android.widget.RadioButton
+import android.widget.TextView
+
+import androidx.activity.OnBackPressedCallback
+import androidx.appcompat.app.ActionBar
+import androidx.appcompat.app.AppCompatActivity
+import androidx.appcompat.widget.Toolbar
+import androidx.cursoradapter.widget.CursorAdapter
+import androidx.fragment.app.DialogFragment
+import androidx.preference.PreferenceManager
+
+import com.google.android.material.floatingactionbutton.FloatingActionButton
+import com.google.android.material.snackbar.Snackbar
+
+import com.stoutner.privacybrowser.R
+import com.stoutner.privacybrowser.dialogs.CreateBookmarkDialog.Companion.createBookmark
+import com.stoutner.privacybrowser.dialogs.CreateBookmarkDialog.CreateBookmarkListener
+import com.stoutner.privacybrowser.dialogs.CreateBookmarkFolderDialog.Companion.createBookmarkFolder
+import com.stoutner.privacybrowser.dialogs.CreateBookmarkFolderDialog.CreateBookmarkFolderListener
+import com.stoutner.privacybrowser.dialogs.EditBookmarkDialog.Companion.bookmarkDatabaseId
+import com.stoutner.privacybrowser.dialogs.EditBookmarkDialog.EditBookmarkListener
+import com.stoutner.privacybrowser.dialogs.EditBookmarkFolderDialog.Companion.folderDatabaseId
+import com.stoutner.privacybrowser.dialogs.EditBookmarkFolderDialog.EditBookmarkFolderListener
+import com.stoutner.privacybrowser.dialogs.MoveToFolderDialog.Companion.moveBookmarks
+import com.stoutner.privacybrowser.dialogs.MoveToFolderDialog.MoveToFolderListener
+import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper
+
+import java.io.ByteArrayOutputStream
+import java.util.function.Consumer
+
+// Define the public constants.
+const val CURRENT_FOLDER = "current_folder"
+const val CURRENT_TITLE = "current_title"
+const val CURRENT_FAVORITE_ICON_BYTE_ARRAY = "current_favorite_icon_byte_array"
+
+// Define the private constants.
+private const val CHECKED_BOOKMARKS_ARRAY_LIST = "checked_bookmarks_array_list"
+
+class BookmarksActivity : AppCompatActivity(), CreateBookmarkListener, CreateBookmarkFolderListener, EditBookmarkListener, EditBookmarkFolderListener, MoveToFolderListener {
+    companion object {
+        // Define the public static variables, which are accessed from the bookmarks database view activity.
+        var currentFolder: String = ""
+        var restartFromBookmarksDatabaseViewActivity = false
+    }
+
+    // Define the class variables.
+    private var bookmarksDeletedSnackbar: Snackbar? = null
+    private var closeActivityAfterDismissingSnackbar = false
+    private var contextualActionMode: ActionMode? = null
+
+    // Declare the class variables.
+    private lateinit var appBar: ActionBar
+    private lateinit var bookmarksCursor: Cursor
+    private lateinit var bookmarksCursorAdapter: CursorAdapter
+    private lateinit var bookmarksDatabaseHelper: BookmarksDatabaseHelper
+    private lateinit var bookmarksListView: ListView
+    private lateinit var currentFavoriteIconByteArray: ByteArray
+    private lateinit var moveBookmarkDownMenuItem: MenuItem
+    private lateinit var moveBookmarkUpMenuItem: MenuItem
+    private lateinit var oldFolderNameString: String
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        // Get a handle for the shared preferences.
+        val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
+
+        // Get the preferences.
+        val allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false)
+        val bottomAppBar = sharedPreferences.getBoolean(getString(R.string.bottom_app_bar_key), false)
+
+        // Disable screenshots if not allowed.
+        if (!allowScreenshots) {
+            window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
+        }
+
+        // Run the default commands.
+        super.onCreate(savedInstanceState)
+
+        // Get the intent that launched the activity.
+        val launchingIntent = intent
+
+        // Populate the variables from the launching intent.
+        currentFolder = launchingIntent.getStringExtra(CURRENT_FOLDER)!!
+        val currentTitle = launchingIntent.getStringExtra(CURRENT_TITLE)!!
+        val currentUrl = launchingIntent.getStringExtra(CURRENT_URL)!!
+        currentFavoriteIconByteArray = launchingIntent.getByteArrayExtra(CURRENT_FAVORITE_ICON_BYTE_ARRAY)!!
+
+        /*  TODO.  Test if not needed.
+        // Set the current folder variable.
+        if (launchingIntent.getStringExtra(CURRENT_FOLDER) != null) {  // Set the current folder from the intent.
+            currentFolder = launchingIntent.getStringExtra(CURRENT_FOLDER)
+        } else {  // Set the current folder to be `""`, which is the home folder.
+            currentFolder = ""
+        }
+
+         */
+
+        // Convert the favorite icon byte array to a bitmap.
+        val currentFavoriteIconBitmap = BitmapFactory.decodeByteArray(currentFavoriteIconByteArray, 0, currentFavoriteIconByteArray.size)
+
+        // Set the content according to the app bar position.
+        if (bottomAppBar) {
+            // Set the content view.
+            setContentView(R.layout.bookmarks_bottom_appbar)
+        } else {
+            // `Window.FEATURE_ACTION_MODE_OVERLAY` makes the contextual action mode cover the support action bar.  It must be requested before the content is set.
+            supportRequestWindowFeature(Window.FEATURE_ACTION_MODE_OVERLAY)
+
+            // Set the content view.
+            setContentView(R.layout.bookmarks_top_appbar)
+        }
+
+        // Get handles for the views.
+        val toolbar = findViewById<Toolbar>(R.id.bookmarks_toolbar)
+        bookmarksListView = findViewById(R.id.bookmarks_listview)
+        val createBookmarkFolderFab = findViewById<FloatingActionButton>(R.id.create_bookmark_folder_fab)
+        val createBookmarkFab = findViewById<FloatingActionButton>(R.id.create_bookmark_fab)
+
+        // Set the support action bar.
+        setSupportActionBar(toolbar)
+
+        // Get a handle for the app bar.
+        appBar = supportActionBar!!
+
+        // Display the home arrow on the app bar.
+        appBar.setDisplayHomeAsUpEnabled(true)
+
+        // Control what the system back command does.
+        val onBackPressedCallback: OnBackPressedCallback = object : OnBackPressedCallback(true) {
+            override fun handleOnBackPressed() {
+                // Prepare to finish the activity.
+                prepareFinish()
+            }
+        }
+
+        // Register the on back pressed callback.
+        onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
+
+        // Initialize the database helper.
+        bookmarksDatabaseHelper = BookmarksDatabaseHelper(this)
+
+        // Set a listener so that tapping a list item loads the URL or folder.
+        bookmarksListView.onItemClickListener = AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, _: Int, id: Long ->
+            // Convert the id from long to int to match the format of the bookmarks database.
+            val databaseId = id.toInt()
+
+            // Get the bookmark cursor for this ID.
+            val bookmarkCursor = bookmarksDatabaseHelper.getBookmark(databaseId)
+
+            // Move the cursor to the first entry.
+            bookmarkCursor.moveToFirst()
+
+            // Act upon the bookmark according to the type.
+            if (bookmarkCursor.getInt(bookmarkCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.IS_FOLDER)) == 1) {  // The selected bookmark is a folder.
+                // Update the current folder.
+                currentFolder = bookmarkCursor.getString(bookmarkCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_NAME))
+
+                // Load the new folder.
+                loadFolder()
+            } else {  // The selected bookmark is not a folder.
+                // Instantiate the edit bookmark dialog.
+                val editBookmarkDialog: DialogFragment = bookmarkDatabaseId(databaseId, currentFavoriteIconBitmap)
+
+                // Make it so.
+                editBookmarkDialog.show(supportFragmentManager, resources.getString(R.string.edit_bookmark))
+            }
+
+            // Close the cursor.
+            bookmarkCursor.close()
+        }
+
+        // Handle long-presses on the list view.
+        bookmarksListView.setMultiChoiceModeListener(object : MultiChoiceModeListener {
+            // Define the object variables.
+            private var deletingBookmarks = false
+
+            // Declare the object variables.
+            private lateinit var editBookmarkMenuItem: MenuItem
+            private lateinit var deleteBookmarksMenuItem: MenuItem
+            private lateinit var selectAllBookmarksMenuItem: MenuItem
+
+            override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
+                // Inflate the menu for the contextual app bar.
+                menuInflater.inflate(R.menu.bookmarks_context_menu, menu)
+
+                // Set the title.
+                if (currentFolder.isEmpty()) {  // Use `R.string.bookmarks` if in the home folder.
+                    mode.setTitle(R.string.bookmarks)
+                } else {  // Use the current folder name as the title.
+                    mode.title = currentFolder
+                }
+
+                // Get handles for menu items that need to be selectively disabled.
+                moveBookmarkUpMenuItem = menu.findItem(R.id.move_bookmark_up)
+                moveBookmarkDownMenuItem = menu.findItem(R.id.move_bookmark_down)
+                editBookmarkMenuItem = menu.findItem(R.id.edit_bookmark)
+                deleteBookmarksMenuItem = menu.findItem(R.id.delete_bookmark)
+                selectAllBookmarksMenuItem = menu.findItem(R.id.context_menu_select_all_bookmarks)
+
+                // Disable the delete bookmarks menu item if a delete is pending.
+                deleteBookmarksMenuItem.isEnabled = !deletingBookmarks
+
+                // Store a handle for the contextual action bar so it can be closed programatically.
+                contextualActionMode = mode
+
+                // Make it so.
+                return true
+            }
+
+            override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
+                // Get a handle for the move to folder menu item.
+                val moveToFolderMenuItem = menu.findItem(R.id.move_to_folder)
+
+                // Get a Cursor with all of the folders.
+                val folderCursor = bookmarksDatabaseHelper.allFolders
+
+                // Display the move to folder menu item if at least one folder exists.
+                moveToFolderMenuItem.isVisible = folderCursor.count > 0
+
+                // Make it so.
+                return true
+            }
+
+            override fun onItemCheckedStateChanged(mode: ActionMode, position: Int, id: Long, checked: Boolean) {
+                // Get the number of selected bookmarks.
+                val numberOfSelectedBookmarks = bookmarksListView.checkedItemCount
+
+                // Only process commands if at least one bookmark is selected.  Otherwise, a context menu with 0 selected bookmarks is briefly displayed.
+                if (numberOfSelectedBookmarks > 0) {
+                    // Adjust the action mode and the menu according to the number of selected bookmarks.
+                    if (numberOfSelectedBookmarks == 1) {  // One bookmark is selected.
+                        // Show the applicable menu items.
+                        moveBookmarkUpMenuItem.isVisible = true
+                        moveBookmarkDownMenuItem.isVisible = true
+                        editBookmarkMenuItem.isVisible = true
+
+                        // Update the enabled status of the move icons.
+                        updateMoveIcons()
+                    } else {  // More than one bookmark is selected.
+                        // Hide non-applicable `MenuItems`.
+                        moveBookmarkUpMenuItem.isVisible = false
+                        moveBookmarkDownMenuItem.isVisible = false
+                        editBookmarkMenuItem.isVisible = false
+                    }
+
+                    // List the number of selected bookmarks in the subtitle.
+                    mode.subtitle = getString(R.string.selected, numberOfSelectedBookmarks)
+
+                    // Show the select all menu item if all the bookmarks are not selected.
+                    selectAllBookmarksMenuItem.isVisible = (numberOfSelectedBookmarks != bookmarksListView.count)
+                }
+            }
+
+            override fun onActionItemClicked(actionMode: ActionMode, menuItem: MenuItem): Boolean {
+                // Declare the variables.
+                val selectedBookmarkNewPosition: Int
+                val selectedBookmarksPositionsSparseBooleanArray: SparseBooleanArray
+
+                // Initialize the selected bookmark position.
+                var selectedBookmarkPosition = 0
+
+                // Get the menu item ID.
+                val menuItemId = menuItem.itemId
+
+                // Run the commands according to the selected action item.
+                if (menuItemId == R.id.move_bookmark_up) {  // Move the bookmark up.
+                    // Get the array of checked bookmark positions.
+                    selectedBookmarksPositionsSparseBooleanArray = bookmarksListView.checkedItemPositions
+
+                    // Get the position of the bookmark that is selected.  If other bookmarks have previously been selected they will be included in the sparse boolean array with a value of `false`.
+                    for (i in 0 until selectedBookmarksPositionsSparseBooleanArray.size()) {
+                        // Check to see if the value for the bookmark is true, meaning it is currently selected.
+                        if (selectedBookmarksPositionsSparseBooleanArray.valueAt(i)) {
+                            // Only one bookmark should have a value of `true` when move bookmark up is enabled.
+                            selectedBookmarkPosition = selectedBookmarksPositionsSparseBooleanArray.keyAt(i)
+                        }
+                    }
+
+                    // Calculate the new position of the selected bookmark.
+                    selectedBookmarkNewPosition = selectedBookmarkPosition - 1
+
+                    // Iterate through the bookmarks.
+                    for (i in 0 until bookmarksListView.count) {
+                        // Get the database ID for the current bookmark.
+                        val currentBookmarkDatabaseId = bookmarksListView.getItemIdAtPosition(i).toInt()
+
+                        // Update the display order for the current bookmark.
+                        if (i == selectedBookmarkPosition) {  // The current bookmark is the selected bookmark.
+                            // Move the current bookmark up one.
+                            bookmarksDatabaseHelper.updateDisplayOrder(currentBookmarkDatabaseId, i - 1)
+                        } else if ((i + 1) == selectedBookmarkPosition) {  // The current bookmark is immediately above the selected bookmark.
+                            // Move the current bookmark down one.
+                            bookmarksDatabaseHelper.updateDisplayOrder(currentBookmarkDatabaseId, i + 1)
+                        } else {  // The current bookmark is not changing positions.
+                            // Move the bookmarks cursor to the current bookmark position.
+                            bookmarksCursor.moveToPosition(i)
+
+                            // Update the display order only if it is not correct in the database.  This fixes problems where the display order somehow got out of sync.
+                            if (bookmarksCursor.getInt(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.DISPLAY_ORDER)) != i)
+                                bookmarksDatabaseHelper.updateDisplayOrder(currentBookmarkDatabaseId, i)
+                        }
+                    }
+
+                    // Update the bookmarks cursor with the current contents of the bookmarks database.
+                    bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolder)
+
+                    // Update the list view.
+                    bookmarksCursorAdapter.changeCursor(bookmarksCursor)
+
+                    // Scroll to the new bookmark position.
+                    scrollBookmarks(selectedBookmarkNewPosition)
+
+                    // Update the enabled status of the move icons.
+                    updateMoveIcons()
+                } else if (menuItemId == R.id.move_bookmark_down) {  // Move the bookmark down.
+                    // Get the array of checked bookmark positions.
+                    selectedBookmarksPositionsSparseBooleanArray = bookmarksListView.checkedItemPositions
+
+                    // Get the position of the bookmark that is selected.  If other bookmarks have previously been selected they will be included in the sparse boolean array with a value of `false`.
+                    for (i in 0 until selectedBookmarksPositionsSparseBooleanArray.size()) {
+                        // Check to see if the value for the bookmark is true, meaning it is currently selected.
+                        if (selectedBookmarksPositionsSparseBooleanArray.valueAt(i)) {
+                            // Only one bookmark should have a value of `true` when move bookmark down is enabled.
+                            selectedBookmarkPosition = selectedBookmarksPositionsSparseBooleanArray.keyAt(i)
+                        }
+                    }
+
+                    // Calculate the new position of the selected bookmark.
+                    selectedBookmarkNewPosition = selectedBookmarkPosition + 1
+
+                    // Iterate through the bookmarks.
+                    for (i in 0 until bookmarksListView.count) {
+                        // Get the database ID for the current bookmark.
+                        val currentBookmarkDatabaseId = bookmarksListView.getItemIdAtPosition(i).toInt()
+
+                        // Update the display order for the current bookmark.
+                        if (i == selectedBookmarkPosition) {  // The current bookmark is the selected bookmark.
+                            // Move the current bookmark down one.
+                            bookmarksDatabaseHelper.updateDisplayOrder(currentBookmarkDatabaseId, i + 1)
+                        } else if (i - 1 == selectedBookmarkPosition) {  // The current bookmark is immediately below the selected bookmark.
+                            // Move the bookmark below the selected bookmark up one.
+                            bookmarksDatabaseHelper.updateDisplayOrder(currentBookmarkDatabaseId, i - 1)
+                        } else {  // The current bookmark is not changing positions.
+                            // Move the bookmarks cursor to the current bookmark position.
+                            bookmarksCursor.moveToPosition(i)
+
+                            // Update the display order only if it is not correct in the database.  This fixes problems where the display order somehow got out of sync.
+                            if (bookmarksCursor.getInt(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.DISPLAY_ORDER)) != i) {
+                                bookmarksDatabaseHelper.updateDisplayOrder(currentBookmarkDatabaseId, i)
+                            }
+                        }
+                    }
+
+                    // Update the bookmarks cursor with the current contents of the bookmarks database.
+                    bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolder)
+
+                    // Update the list view.
+                    bookmarksCursorAdapter.changeCursor(bookmarksCursor)
+
+                    // Scroll to the new bookmark position.
+                    scrollBookmarks(selectedBookmarkNewPosition)
+
+                    // Update the enabled status of the move icons.
+                    updateMoveIcons()
+                } else if (menuItemId == R.id.move_to_folder) {  // Move to folder.
+                    // Instantiate the move to folder alert dialog.
+                    val moveToFolderDialog: DialogFragment = moveBookmarks(currentFolder, bookmarksListView.checkedItemIds)
+
+                    // Show the move to folder alert dialog.
+                    moveToFolderDialog.show(supportFragmentManager, resources.getString(R.string.move_to_folder))
+                } else if (menuItemId == R.id.edit_bookmark) {
+                    // Get the array of checked bookmark positions.
+                    selectedBookmarksPositionsSparseBooleanArray = bookmarksListView.checkedItemPositions
+
+                    // Get the position of the bookmark that is selected.  If other bookmarks have previously been selected they will be included in the sparse boolean array with a value of `false`.
+                    for (i in 0 until selectedBookmarksPositionsSparseBooleanArray.size()) {
+                        // Check to see if the value for the bookmark is true, meaning it is currently selected.
+                        if (selectedBookmarksPositionsSparseBooleanArray.valueAt(i)) {
+                            // Only one bookmark should have a value of `true` when move edit bookmark is enabled.
+                            selectedBookmarkPosition = selectedBookmarksPositionsSparseBooleanArray.keyAt(i)
+                        }
+                    }
+
+                    // Move the cursor to the selected position.
+                    bookmarksCursor.moveToPosition(selectedBookmarkPosition)
+
+                    // Get the selected bookmark database ID.
+                    val databaseId = bookmarksCursor.getInt(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.ID))
+
+                    // Show the edit bookmark or edit bookmark folder dialog.
+                    if (bookmarksCursor.getInt(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.IS_FOLDER)) == 1) {  // A folder is selected.
+                        // Save the current folder name, which is used in `onSaveBookmarkFolder()`.
+                        oldFolderNameString = bookmarksCursor.getString(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_NAME))
+
+                        // Instantiate the edit bookmark folder dialog.
+                        val editFolderDialog: DialogFragment = folderDatabaseId(databaseId, currentFavoriteIconBitmap)
+
+                        // Make it so.
+                        editFolderDialog.show(supportFragmentManager, resources.getString(R.string.edit_folder))
+                    } else {  // A bookmark is selected.
+                        // Instantiate the edit bookmark dialog.
+                        val editBookmarkDialog: DialogFragment = bookmarkDatabaseId(databaseId, currentFavoriteIconBitmap)
+
+                        // Make it so.
+                        editBookmarkDialog.show(supportFragmentManager, resources.getString(R.string.edit_bookmark))
+                    }
+                } else if (menuItemId == R.id.delete_bookmark) {  // Delete bookmark.
+                    // Set the deleting bookmarks flag, which prevents the delete menu item from being enabled until the current process finishes.
+                    deletingBookmarks = true
+
+                    // Get an array of the selected row IDs.
+                    val selectedBookmarksIdsLongArray = bookmarksListView.checkedItemIds
+
+                    // Initialize a variable to count the number of bookmarks to delete.
+                    var numberOfBookmarksToDelete = 0
+
+                    // Count the number of bookmarks to delete.
+                    for (databaseIdLong in selectedBookmarksIdsLongArray) {
+                        // Convert the database ID long to an int.
+                        val databaseIdInt = databaseIdLong.toInt()
+
+                        // Count the contents of the folder if the selected bookmark is a folder.
+                        if (bookmarksDatabaseHelper.isFolder(databaseIdInt)) {
+                            // Add the bookmarks from the folder to the running total.
+                            numberOfBookmarksToDelete += countBookmarkFolderContents(databaseIdInt)
+                        }
+
+                        // Increment the count of the number of bookmarks to delete.
+                        numberOfBookmarksToDelete++
+                    }
+
+                    // Get an array of checked bookmarks.  `.clone()` makes a copy that won't change if the list view is reloaded, which is needed for re-selecting the bookmarks on undelete.
+                    selectedBookmarksPositionsSparseBooleanArray = bookmarksListView.checkedItemPositions.clone()
+
+                    // Update the bookmarks cursor with the current contents of the bookmarks database except for the specified database IDs.
+                    bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrderExcept(selectedBookmarksIdsLongArray, currentFolder)
+
+                    // Update the list view.
+                    bookmarksCursorAdapter.changeCursor(bookmarksCursor)
+
+                    // Create a Snackbar with the number of deleted bookmarks.
+                    bookmarksDeletedSnackbar = Snackbar.make(findViewById(R.id.bookmarks_coordinatorlayout), getString(R.string.bookmarks_deleted, numberOfBookmarksToDelete), Snackbar.LENGTH_LONG)
+                        .setAction(R.string.undo) { }  // Do nothing because everything will be handled by `onDismissed()` below.
+                        .addCallback(object : Snackbar.Callback() {
+                            @SuppressLint("SwitchIntDef")  // Ignore the lint warning about not handling the other possible events as they are covered by `default:`.
+                            override fun onDismissed(snackbar: Snackbar, event: Int) {
+                                if (event == 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 (i in 0 until selectedBookmarksPositionsSparseBooleanArray.size())
+                                        bookmarksListView.setItemChecked(selectedBookmarksPositionsSparseBooleanArray.keyAt(i), true)
+                                } else {  // The snackbar was dismissed without the undo button being pushed.
+                                    // Delete each selected bookmark.
+                                    for (databaseIdLong in selectedBookmarksIdsLongArray) {
+                                        // Convert the database long ID to an int.
+                                        val databaseIdInt = databaseIdLong.toInt()
+
+                                        // 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)
+                                    }
+
+                                    // Update the display order.
+                                    for (i in 0 until bookmarksListView.count) {
+                                        // Get the database ID for the current bookmark.
+                                        val currentBookmarkDatabaseId = bookmarksListView.getItemIdAtPosition(i).toInt()
+
+                                        // Move bookmarks cursor 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.getColumnIndexOrThrow(BookmarksDatabaseHelper.DISPLAY_ORDER)) != i)
+                                            bookmarksDatabaseHelper.updateDisplayOrder(currentBookmarkDatabaseId, i)
+                                    }
+                                }
+
+                                // Reset the deleting bookmarks flag.
+                                deletingBookmarks = false
+
+                                // Enable the delete bookmarks menu item.
+                                deleteBookmarksMenuItem.isEnabled = true
+
+                                // Close the activity if back has been pressed.
+                                if (closeActivityAfterDismissingSnackbar)
+                                    finish()
+                            }
+                        })
+
+                    // Show the Snackbar.
+                    bookmarksDeletedSnackbar!!.show()
+                } else if (menuItemId == R.id.context_menu_select_all_bookmarks) {  // Select all.
+                    // Get the total number of bookmarks.
+                    val numberOfBookmarks = bookmarksListView.count
+
+                    // Select them all.
+                    for (i in 0 until numberOfBookmarks) {
+                        bookmarksListView.setItemChecked(i, true)
+                    }
+                }
+
+                // Consume the click.
+                return true
+            }
+
+            override fun onDestroyActionMode(mode: ActionMode) {
+                // Do nothing.
+            }
+        })
+
+        // Set the create new bookmark folder FAB to display the alert dialog.
+        createBookmarkFolderFab.setOnClickListener {
+            // Create a create bookmark folder dialog.
+            val createBookmarkFolderDialog: DialogFragment = createBookmarkFolder(currentFavoriteIconBitmap)
+
+            // Show the create bookmark folder dialog.
+            createBookmarkFolderDialog.show(supportFragmentManager, getString(R.string.create_folder))
+        }
+
+        // Set the create new bookmark FAB to display the alert dialog.
+        createBookmarkFab.setOnClickListener {
+            // Instantiate the create bookmark dialog.
+            val createBookmarkDialog: DialogFragment = createBookmark(currentUrl, currentTitle, currentFavoriteIconBitmap)
+
+            // Display the create bookmark dialog.
+            createBookmarkDialog.show(supportFragmentManager, resources.getString(R.string.create_bookmark))
+        }
+
+        // Restore the state if the app has been restarted.
+        if (savedInstanceState != null) {
+            // Restore the current folder.
+            currentFolder = savedInstanceState.getString(CURRENT_FOLDER)!!
+
+            // Update the bookmarks list view after it has loaded.
+            bookmarksListView.post {
+                // Get the checked bookmarks array list.
+                val checkedBookmarksArrayList = savedInstanceState.getIntegerArrayList(CHECKED_BOOKMARKS_ARRAY_LIST)!!
+
+                // Check each previously checked bookmark in the list view.
+                checkedBookmarksArrayList.forEach(Consumer { position: Int -> bookmarksListView.setItemChecked(position, true) })
+            }
+        }
+
+        // Load the current folder.
+        loadFolder()
+    }
+
+    public override fun onRestart() {
+        // Run the default commands.
+        super.onRestart()
+
+        // Update the list view if returning from the bookmarks database view activity.
+        if (restartFromBookmarksDatabaseViewActivity) {
+            // Load the current folder in the list view.
+            loadFolder()
+
+            // Reset the restart from bookmarks database view activity flag.
+            restartFromBookmarksDatabaseViewActivity = false
+        }
+    }
+
+    public override fun onSaveInstanceState(savedInstanceState: Bundle) {
+        // Run the default commands.
+        super.onSaveInstanceState(savedInstanceState)
+
+        // Get the sparse boolean array of the checked items.
+        val checkedBookmarksSparseBooleanArray = bookmarksListView.checkedItemPositions
+
+        // Create a checked items array list.
+        val checkedBookmarksArrayList = ArrayList<Int>()
+
+        // Add each checked bookmark position to the array list.
+        for (i in 0 until checkedBookmarksSparseBooleanArray.size()) {
+            // Check to see if the bookmark is currently checked.  Bookmarks that have previously been checked but currently aren't will be populated in the sparse boolean array, but will return false.
+            if (checkedBookmarksSparseBooleanArray.valueAt(i)) {
+                // Add the bookmark position to the checked bookmarks array list.
+                checkedBookmarksArrayList.add(checkedBookmarksSparseBooleanArray.keyAt(i))
+            }
+        }
+
+        // Store the variables in the saved instance state.
+        savedInstanceState.putString(CURRENT_FOLDER, currentFolder)
+        savedInstanceState.putIntegerArrayList(CHECKED_BOOKMARKS_ARRAY_LIST, checkedBookmarksArrayList)
+    }
+
+    override fun onCreateOptionsMenu(menu: Menu): Boolean {
+        // Inflate the menu.
+        menuInflater.inflate(R.menu.bookmarks_options_menu, menu)
+
+        // Success.
+        return true
+    }
+
+    override fun onOptionsItemSelected(menuItem: MenuItem): Boolean {
+        // Get a handle for the menu item ID.
+        val menuItemId = menuItem.itemId
+
+        // Run the command according to the selected option.
+        if (menuItemId == android.R.id.home) {  // Home.  The home arrow is identified as `android.R.id.home`, not just `R.id.home`.
+            if (currentFolder.isEmpty()) {  // Currently in the home folder.
+                // Prepare to finish the activity.
+                prepareFinish()
+            } else {  // Currently in a subfolder.
+                // Set the former parent folder as the current folder.
+                currentFolder = bookmarksDatabaseHelper.getParentFolderName(currentFolder)
+
+                // Load the new current folder.
+                loadFolder()
+            }
+        } else if (menuItemId == R.id.options_menu_select_all_bookmarks) {  // Select all.
+            // Get the total number of bookmarks.
+            val numberOfBookmarks = bookmarksListView.count
+
+            // Select them all.
+            for (i in 0 until numberOfBookmarks) {
+                bookmarksListView.setItemChecked(i, true)
+            }
+        } else if (menuItemId == R.id.bookmarks_database_view) {
+            // Close the contextual action bar if it is displayed.  This can happen if the bottom app bar is enabled.
+            contextualActionMode?.finish()
+
+            // Create an intent to launch the bookmarks database view activity.
+            val bookmarksDatabaseViewIntent = Intent(this, BookmarksDatabaseViewActivity::class.java)
+
+            // Include the favorite icon byte array to the intent.
+            bookmarksDatabaseViewIntent.putExtra(CURRENT_FAVORITE_ICON_BYTE_ARRAY, currentFavoriteIconByteArray)
+
+            // Make it so.
+            startActivity(bookmarksDatabaseViewIntent)
+        }
+        return true
+    }
+
+    override fun onCreateBookmark(dialogFragment: DialogFragment, favoriteIconBitmap: Bitmap) {
+        // Get the alert dialog from the fragment.
+        val dialog = dialogFragment.dialog!!
+
+        // Get the views from the dialog fragment.
+        val createBookmarkNameEditText = dialog.findViewById<EditText>(R.id.create_bookmark_name_edittext)
+        val createBookmarkUrlEditText = dialog.findViewById<EditText>(R.id.create_bookmark_url_edittext)
+
+        // Extract the strings from the edit texts.
+        val bookmarkNameString = createBookmarkNameEditText.text.toString()
+        val bookmarkUrlString = createBookmarkUrlEditText.text.toString()
+
+        // Create a favorite icon byte array output stream.
+        val favoriteIconByteArrayOutputStream = ByteArrayOutputStream()
+
+        // Convert the favorite icon bitmap to a byte array.  `0` is for lossless compression (the only option for a PNG).
+        favoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, favoriteIconByteArrayOutputStream)
+
+        // Convert the favorite icon byte array stream to a byte array.
+        val favoriteIconByteArray = favoriteIconByteArrayOutputStream.toByteArray()
+
+        // Display the new bookmark below the current items in the (0 indexed) list.
+        val newBookmarkDisplayOrder = bookmarksListView.count
+
+        // Create the bookmark.
+        bookmarksDatabaseHelper.createBookmark(bookmarkNameString, bookmarkUrlString, currentFolder, newBookmarkDisplayOrder, favoriteIconByteArray)
+
+        // Update the bookmarks cursor with the current contents of this folder.
+        bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolder)
+
+        // Update the list view.
+        bookmarksCursorAdapter.changeCursor(bookmarksCursor)
+
+        // Scroll to the new bookmark.
+        bookmarksListView.setSelection(newBookmarkDisplayOrder)
+    }
+
+    override fun onCreateBookmarkFolder(dialogFragment: DialogFragment, favoriteIconBitmap: Bitmap) {
+        // Get the dialog from the dialog fragment.
+        val dialog = dialogFragment.dialog!!
+
+        // Get handles for the views in the dialog fragment.
+        val folderNameEditText = dialog.findViewById<EditText>(R.id.folder_name_edittext)
+        val defaultIconRadioButton = dialog.findViewById<RadioButton>(R.id.default_icon_radiobutton)
+        val defaultIconImageView = dialog.findViewById<ImageView>(R.id.default_icon_imageview)
+
+        // Get new folder name string.
+        val folderNameString = folderNameEditText.text.toString()
+
+        // Set the folder icon bitmap according to the dialog.
+        val folderIconBitmap = if (defaultIconRadioButton.isChecked) {  // Use the default folder icon.
+            // Get the default folder icon drawable.
+            val folderIconDrawable = defaultIconImageView.drawable
+
+            // Convert the folder icon drawable to a bitmap drawable.
+            val folderIconBitmapDrawable = folderIconDrawable as BitmapDrawable
+
+            // Convert the folder icon bitmap drawable to a bitmap.
+            folderIconBitmapDrawable.bitmap
+        } else {  // Use the WebView favorite icon.
+            // Copy the favorite icon bitmap to the folder icon bitmap.
+            favoriteIconBitmap
+        }
+
+        // Create a folder icon byte array output stream.
+        val folderIconByteArrayOutputStream = ByteArrayOutputStream()
+
+        // Convert the folder icon bitmap to a byte array.  `0` is for lossless compression (the only option for a PNG).
+        folderIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, folderIconByteArrayOutputStream)
+
+        // Convert the folder icon byte array stream to a byte array.
+        val folderIconByteArray = folderIconByteArrayOutputStream.toByteArray()
+
+        // Move all the bookmarks down one in the display order.
+        for (i in 0 until bookmarksListView.count) {
+            val databaseId = bookmarksListView.getItemIdAtPosition(i).toInt()
+            bookmarksDatabaseHelper.updateDisplayOrder(databaseId, i + 1)
+        }
+
+        // Create the folder, which will be placed at the top of the list view.
+        bookmarksDatabaseHelper.createFolder(folderNameString, currentFolder, folderIconByteArray)
+
+        // Update the bookmarks cursor with the contents of the current folder.
+        bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolder)
+
+        // Update the list view.
+        bookmarksCursorAdapter.changeCursor(bookmarksCursor)
+
+        // Scroll to the new folder.
+        bookmarksListView.setSelection(0)
+    }
+
+    override fun onSaveBookmark(dialogFragment: DialogFragment, selectedBookmarkDatabaseId: Int, favoriteIconBitmap: Bitmap) {
+        // Get the dialog from the dialog fragment.
+        val dialog = dialogFragment.dialog!!
+
+        // Get handles for the views from the dialog fragment.
+        val bookmarkNameEditText = dialog.findViewById<EditText>(R.id.bookmark_name_edittext)
+        val bookmarkUrlEditText = dialog.findViewById<EditText>(R.id.bookmark_url_edittext)
+        val currentIconRadioButton = dialog.findViewById<RadioButton>(R.id.current_icon_radiobutton)
+
+        // Get the bookmark strings.
+        val bookmarkNameString = bookmarkNameEditText.text.toString()
+        val bookmarkUrlString = bookmarkUrlEditText.text.toString()
+
+        // Update the bookmark.
+        if (currentIconRadioButton.isChecked) {  // Update the bookmark without changing the favorite icon.
+            bookmarksDatabaseHelper.updateBookmark(selectedBookmarkDatabaseId, bookmarkNameString, bookmarkUrlString)
+        } else {  // Update the bookmark using the WebView favorite icon.
+            // Create a favorite icon byte array output stream.
+            val newFavoriteIconByteArrayOutputStream = ByteArrayOutputStream()
+
+            // Convert the favorite icon bitmap to a byte array.  `0` is for lossless compression (the only option for a PNG).
+            favoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, newFavoriteIconByteArrayOutputStream)
+
+            // Convert the favorite icon byte array stream to a byte array.
+            val newFavoriteIconByteArray = newFavoriteIconByteArrayOutputStream.toByteArray()
+
+            //  Update the bookmark and the favorite icon.
+            bookmarksDatabaseHelper.updateBookmark(selectedBookmarkDatabaseId, bookmarkNameString, bookmarkUrlString, newFavoriteIconByteArray)
+        }
+
+        // Close the contextual action bar if it is displayed.
+        contextualActionMode?.finish()
+
+        // Update the bookmarks cursor with the contents of the current folder.
+        bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolder)
+
+        // Update the list view.
+        bookmarksCursorAdapter.changeCursor(bookmarksCursor)
+    }
+
+    override fun onSaveBookmarkFolder(dialogFragment: DialogFragment, selectedFolderDatabaseId: Int, favoriteIconBitmap: Bitmap) {
+        // Get the dialog from the dialog fragment.
+        val dialog = dialogFragment.dialog!!
+
+        // Get handles for the views from the dialog fragment.
+        val currentFolderIconRadioButton = dialog.findViewById<RadioButton>(R.id.current_icon_radiobutton)
+        val defaultFolderIconRadioButton = dialog.findViewById<RadioButton>(R.id.default_icon_radiobutton)
+        val defaultFolderIconImageView = dialog.findViewById<ImageView>(R.id.default_icon_imageview)
+        val editFolderNameEditText = dialog.findViewById<EditText>(R.id.folder_name_edittext)
+
+        // Get the new folder name.
+        val newFolderNameString = editFolderNameEditText.text.toString()
+
+        // Check if the favorite icon has changed.
+        if (currentFolderIconRadioButton.isChecked) {  // Only the name has changed.
+            // Update the name in the database.
+            bookmarksDatabaseHelper.updateFolder(selectedFolderDatabaseId, oldFolderNameString, newFolderNameString)
+        } else {  // The icon has changed.  TODO:  Test.
+            // Populate the new folder icon bitmap.
+            val folderIconBitmap: Bitmap = if (defaultFolderIconRadioButton.isChecked) {
+                // Get the default folder icon drawable.
+                val folderIconDrawable = defaultFolderIconImageView.drawable
+
+                // Convert the folder icon drawable to a bitmap drawable.
+                val folderIconBitmapDrawable = folderIconDrawable as BitmapDrawable
+
+                // Convert the folder icon bitmap drawable to a bitmap.
+                folderIconBitmapDrawable.bitmap
+            } else {  // Use the WebView favorite icon.
+                // Copy the favorite icon bitmap to the folder icon bitmap.
+                favoriteIconBitmap
+            }
+
+            // Create a folder icon byte array output stream.
+            val newFolderIconByteArrayOutputStream = ByteArrayOutputStream()
+
+            // Convert the folder icon bitmap to a byte array.  `0` is for lossless compression (the only option for a PNG).
+            folderIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, newFolderIconByteArrayOutputStream)
+
+            // Convert the folder icon byte array stream to a byte array.
+            val newFolderIconByteArray = newFolderIconByteArrayOutputStream.toByteArray()
+
+            // Update the database.
+            if (!currentFolderIconRadioButton.isChecked && newFolderNameString == oldFolderNameString)  // Only the icon has changed.
+                bookmarksDatabaseHelper.updateFolder(selectedFolderDatabaseId, newFolderIconByteArray)
+            else  // The folder icon and the name have changed.
+                bookmarksDatabaseHelper.updateFolder(selectedFolderDatabaseId, oldFolderNameString, newFolderNameString, newFolderIconByteArray)
+        }
+
+        // Update the bookmarks cursor with the current contents of this folder.
+        bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolder)
+
+        // Update the list view.
+        bookmarksCursorAdapter.changeCursor(bookmarksCursor)
+
+        // Close the contextual action mode.
+        contextualActionMode!!.finish()
+    }
+
+    override fun onMoveToFolder(dialogFragment: DialogFragment) {
+        // Get the dialog from the dialog fragment.
+        val dialog = dialogFragment.dialog!!
+
+        // Get a handle for the folder list view from the dialog.
+        val folderListView = dialog.findViewById<ListView>(R.id.move_to_folder_listview)
+
+        // Store a long array of the selected folders.
+        val newFolderLongArray = folderListView.checkedItemIds
+
+        // Get the new folder database ID.  Only one folder will be selected so it will be the first one.
+        val newFolderDatabaseId = newFolderLongArray[0].toInt()
+
+        // Set the new folder name.
+        val newFolderName = if (newFolderDatabaseId == 0) {
+            // The new folder is the home folder, represented as `""` in the database.
+            ""
+        } else {
+            // Get the new folder name from the database.
+            bookmarksDatabaseHelper.getFolderName(newFolderDatabaseId)
+        }
+
+        // Get a long array with the the database ID of the selected bookmarks.
+        val selectedBookmarksLongArray = bookmarksListView.checkedItemIds
+
+        // Move each of the selected bookmarks to the new folder.
+        for (databaseIdLong in selectedBookmarksLongArray) {
+            // Convert the database long ID to an int for each selected bookmark.
+            val databaseIdInt = databaseIdLong.toInt()
+
+            // Move the selected bookmark to the new folder.
+            bookmarksDatabaseHelper.moveToFolder(databaseIdInt, newFolderName)
+        }
+
+        // Update the bookmarks cursor with the current contents of this folder.
+        bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolder)
+
+        // Update the list view.
+        bookmarksCursorAdapter.changeCursor(bookmarksCursor)
+
+        // Close the contextual app bar.
+        contextualActionMode!!.finish()
+    }
+
+    private fun countBookmarkFolderContents(folderDatabaseId: Int): Int {
+        // Get the name of the folder.
+        val folderName = bookmarksDatabaseHelper.getFolderName(folderDatabaseId)
+
+        // Get the contents of the folder.
+        val folderCursor = bookmarksDatabaseHelper.getBookmarkIds(folderName)
+
+        // Initialize the bookmark counter.
+        var bookmarkCounter = 0
+
+        // Count each of the bookmarks in the folder.
+        for (i in 0 until folderCursor.count) {
+            // Move the folder cursor to the current row.
+            folderCursor.moveToPosition(i)
+
+            // Get the database ID of the item.
+            val itemDatabaseId = folderCursor.getInt(folderCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.ID))
+
+            // If this is a folder, recursively count the contents first.
+            if (bookmarksDatabaseHelper.isFolder(itemDatabaseId))
+                bookmarkCounter += countBookmarkFolderContents(itemDatabaseId)
+
+            // Add the bookmark to the running total.
+            bookmarkCounter++
+        }
+
+        // Return the bookmark counter.
+        return bookmarkCounter
+    }
+
+    private fun deleteBookmarkFolderContents(folderDatabaseId: Int) {
+        // Get the name of the folder.
+        val folderName = bookmarksDatabaseHelper.getFolderName(folderDatabaseId)
+
+        // Get the contents of the folder.
+        val folderCursor = bookmarksDatabaseHelper.getBookmarkIds(folderName)
+
+        // Delete each of the bookmarks in the folder.
+        for (i in 0 until folderCursor.count) {
+            // Move the folder cursor to the current row.
+            folderCursor.moveToPosition(i)
+
+            // Get the database ID of the item.
+            val itemDatabaseId = folderCursor.getInt(folderCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.ID))
+
+            // If this is a folder, recursively delete the contents first.
+            if (bookmarksDatabaseHelper.isFolder(itemDatabaseId))
+                deleteBookmarkFolderContents(itemDatabaseId)
+
+            // Delete the bookmark.
+            bookmarksDatabaseHelper.deleteBookmark(itemDatabaseId)
+        }
+    }
+
+    private fun prepareFinish() {
+        // Check to see if a snackbar is currently displayed.  If so, it must be closed before exiting so that a pending delete is completed before reloading the list view in the bookmarks drawer.
+        if (bookmarksDeletedSnackbar != null && bookmarksDeletedSnackbar!!.isShown) {  // Close the bookmarks deleted snackbar before going home.
+            // Set the close flag.
+            closeActivityAfterDismissingSnackbar = true
+
+            // Dismiss the snackbar.
+            bookmarksDeletedSnackbar!!.dismiss()
+        } else {  // Go home immediately.
+            // Update the bookmarks folder for the bookmarks drawer in the main WebView activity.
+            MainWebViewActivity.currentBookmarksFolder = currentFolder
+
+            // Close the bookmarks drawer and reload the bookmarks list view when returning to the main WebView activity.
+            MainWebViewActivity.restartFromBookmarksActivity = true
+
+            // Exit the bookmarks activity.
+            finish()
+        }
+    }
+
+    private fun updateMoveIcons() {
+        // Get a long array of the selected bookmarks.
+        val selectedBookmarksLongArray = bookmarksListView.checkedItemIds
+
+        // Get the database IDs for the first, last, and selected bookmarks.
+        val firstBookmarkDatabaseId = bookmarksListView.getItemIdAtPosition(0).toInt()
+        val lastBookmarkDatabaseId = bookmarksListView.getItemIdAtPosition(bookmarksListView.count - 1).toInt()  // The bookmarks list view is 0 indexed.
+        val selectedBookmarkDatabaseId = selectedBookmarksLongArray[0].toInt()
+
+        // Update the move bookmark up menu item.
+        if (selectedBookmarkDatabaseId == firstBookmarkDatabaseId) {  // The selected bookmark is in the first position.
+            // Disable the move bookmark up menu item.
+            moveBookmarkUpMenuItem.isEnabled = false
+
+            //  Set the icon.
+            moveBookmarkUpMenuItem.setIcon(R.drawable.move_up_disabled)
+        } else {  // The selected bookmark is not in the first position.
+            // Enable the move bookmark up menu item.
+            moveBookmarkUpMenuItem.isEnabled = true
+
+            // Set the icon according to the theme.
+            moveBookmarkUpMenuItem.setIcon(R.drawable.move_up_enabled)
+        }
+
+        // Update the move bookmark down menu item.
+        if (selectedBookmarkDatabaseId == lastBookmarkDatabaseId) {  // The selected bookmark is in the last position.
+            // Disable the move bookmark down menu item.
+            moveBookmarkDownMenuItem.isEnabled = false
+
+            // Set the icon.
+            moveBookmarkDownMenuItem.setIcon(R.drawable.move_down_disabled)
+        } else {  // The selected bookmark is not in the last position.
+            // Enable the move bookmark down menu item.
+            moveBookmarkDownMenuItem.isEnabled = true
+
+            // Set the icon.
+            moveBookmarkDownMenuItem.setIcon(R.drawable.move_down_enabled)
+        }
+    }
+
+    private fun scrollBookmarks(selectedBookmarkPosition: Int) {
+        // Get the first and last visible bookmark positions.
+        val firstVisibleBookmarkPosition = bookmarksListView.firstVisiblePosition
+        val lastVisibleBookmarkPosition = bookmarksListView.lastVisiblePosition
+
+        // Calculate the number of bookmarks per screen.
+        val numberOfBookmarksPerScreen = lastVisibleBookmarkPosition - firstVisibleBookmarkPosition
+
+        // Scroll with the moved bookmark if necessary.
+        if (selectedBookmarkPosition <= firstVisibleBookmarkPosition) {  // The selected bookmark position is at or above the top of the screen.
+            // Scroll to the selected bookmark position.
+            bookmarksListView.setSelection(selectedBookmarkPosition)
+        } else if (selectedBookmarkPosition >= lastVisibleBookmarkPosition - 1) {  // The selected bookmark is at or below the bottom of the screen.
+            // The `-1` handles partial bookmarks displayed at the bottom of the list view.  This command scrolls to display the selected bookmark at the bottom of the screen.
+            // `+1` assures that the entire bookmark will be displayed in situations where only a partial bookmark fits at the bottom of the list view.
+            bookmarksListView.setSelection(selectedBookmarkPosition - numberOfBookmarksPerScreen + 1)
+        }
+    }
+
+    private fun loadFolder() {
+        // Update the bookmarks cursor with the contents of the bookmarks database for the current folder.
+        bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolder)
+
+        // Setup a cursor adapter.
+        bookmarksCursorAdapter = object : CursorAdapter(this, bookmarksCursor, false) {
+            override fun newView(context: Context, cursor: Cursor, parent: ViewGroup): View {
+                // Inflate the individual item layout.
+                return layoutInflater.inflate(R.layout.bookmarks_activity_item_linearlayout, parent, false)
+            }
+
+            override fun bindView(view: View, context: Context, cursor: Cursor) {
+                // Get handles for the views.
+                val bookmarkFavoriteIconImageView = view.findViewById<ImageView>(R.id.bookmark_favorite_icon)
+                val bookmarkNameTextView = view.findViewById<TextView>(R.id.bookmark_name)
+
+                // Get the favorite icon byte array from the cursor.
+                val favoriteIconByteArray = cursor.getBlob(cursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.FAVORITE_ICON))
+
+                // Convert the byte array to a bitmap beginning at the first byte and ending at the last.
+                val favoriteIconBitmap = BitmapFactory.decodeByteArray(favoriteIconByteArray, 0, favoriteIconByteArray.size)
+
+                // Display the bitmap in the bookmark favorite icon image view.
+                bookmarkFavoriteIconImageView.setImageBitmap(favoriteIconBitmap)
+
+                // Get the bookmark name from the cursor.
+                val bookmarkNameString = cursor.getString(cursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_NAME))
+
+                // Display the bookmark name.
+                bookmarkNameTextView.text = bookmarkNameString
+
+                // Make the font bold for folders.
+                if (cursor.getInt(cursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.IS_FOLDER)) == 1)
+                    bookmarkNameTextView.typeface = Typeface.DEFAULT_BOLD
+                else  // Reset the font to default for normal bookmarks.
+                    bookmarkNameTextView.typeface = Typeface.DEFAULT
+            }
+        }
+
+        // Populate the list view with the adapter.
+        bookmarksListView.adapter = bookmarksCursorAdapter
+
+        // Set the app bar title.
+        if (currentFolder.isEmpty())
+            appBar.setTitle(R.string.bookmarks)
+        else
+            appBar.title = currentFolder
+    }
+
+    public override fun onDestroy() {
+        // Close the bookmarks cursor and database.
+        bookmarksCursor.close()
+        bookmarksDatabaseHelper.close()
+
+        // Run the default commands.
+        super.onDestroy()
+    }
+}
diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksDatabaseViewActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksDatabaseViewActivity.java
deleted file mode 100644 (file)
index 2309a56..0000000
+++ /dev/null
@@ -1,935 +0,0 @@
-/*
- * Copyright 2016-2023 Soren Stoutner <soren@stoutner.com>.
- *
- * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
- *
- * Privacy Browser Android 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 Android 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 Android.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.stoutner.privacybrowser.activities;
-
-import android.annotation.SuppressLint;
-import android.app.Dialog;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.res.Configuration;
-import android.database.Cursor;
-import android.database.MatrixCursor;
-import android.database.MergeCursor;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Typeface;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.util.SparseBooleanArray;
-import android.view.ActionMode;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.AbsListView;
-import android.widget.AdapterView;
-import android.widget.EditText;
-import android.widget.ImageView;
-import android.widget.ListView;
-import android.widget.RadioButton;
-import android.widget.ResourceCursorAdapter;
-import android.widget.Spinner;
-import android.widget.TextView;
-
-import androidx.activity.OnBackPressedCallback;
-import androidx.annotation.NonNull;
-import androidx.appcompat.app.ActionBar;
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.appcompat.widget.Toolbar;  // The AndroidX toolbar must be used until the minimum API is >= 21.
-import androidx.core.content.ContextCompat;
-import androidx.cursoradapter.widget.CursorAdapter;
-import androidx.fragment.app.DialogFragment;  // The AndroidX dialog fragment must be used or an error is produced on API <=22.
-import androidx.preference.PreferenceManager;
-
-import com.google.android.material.snackbar.Snackbar;
-
-import com.stoutner.privacybrowser.R;
-import com.stoutner.privacybrowser.dialogs.EditBookmarkDatabaseViewDialog;
-import com.stoutner.privacybrowser.dialogs.EditBookmarkFolderDatabaseViewDialog;
-import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper;
-
-import java.io.ByteArrayOutputStream;
-import java.util.Arrays;
-
-public class BookmarksDatabaseViewActivity extends AppCompatActivity implements EditBookmarkDatabaseViewDialog.EditBookmarkDatabaseViewListener,
-        EditBookmarkFolderDatabaseViewDialog.EditBookmarkFolderDatabaseViewListener {
-    // Define the class constants.
-    private static final int ALL_FOLDERS_DATABASE_ID = -2;
-    public static final int HOME_FOLDER_DATABASE_ID = -1;
-
-    // Define the saved instance state constants.
-    private final String CURRENT_FOLDER_DATABASE_ID = "current_folder_database_id";
-    private final String CURRENT_FOLDER_NAME = "current_folder_name";
-    private final String SORT_BY_DISPLAY_ORDER = "sort_by_display_order";
-
-    // Define the class variables.
-    private int currentFolderDatabaseId;
-    private String currentFolderName;
-    private boolean sortByDisplayOrder;
-    private BookmarksDatabaseHelper bookmarksDatabaseHelper;
-    private Cursor bookmarksCursor;
-    private CursorAdapter bookmarksCursorAdapter;
-    private String oldFolderNameString;
-    private Snackbar bookmarksDeletedSnackbar;
-    private boolean closeActivityAfterDismissingSnackbar;
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        // Get a handle for the shared preferences.
-        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
-
-        // Get the preferences.
-        boolean allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false);
-        boolean bottomAppBar = sharedPreferences.getBoolean(getString(R.string.bottom_app_bar_key), false);
-
-        // Disable screenshots if not allowed.
-        if (!allowScreenshots) {
-            getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
-        }
-
-        // Run the default commands.
-        super.onCreate(savedInstanceState);
-
-        // Get the intent that launched the activity.
-        Intent launchingIntent = getIntent();
-
-        // Get the favorite icon byte array.
-        byte[] favoriteIconByteArray = launchingIntent.getByteArrayExtra("favorite_icon_byte_array");
-
-        // Remove the incorrect lint warning below that the favorite icon byte array might be null.
-        assert favoriteIconByteArray != null;
-
-        // Convert the favorite icon byte array to a bitmap and store it in a class variable.
-        Bitmap favoriteIconBitmap = BitmapFactory.decodeByteArray(favoriteIconByteArray, 0, favoriteIconByteArray.length);
-
-        // Set the view according to the theme.
-        if (bottomAppBar) {
-            // Set the content view.
-            setContentView(R.layout.bookmarks_databaseview_bottom_appbar);
-        } else {
-            // `Window.FEATURE_ACTION_MODE_OVERLAY` makes the contextual action mode cover the support action bar.  It must be requested before the content is set.
-            supportRequestWindowFeature(Window.FEATURE_ACTION_MODE_OVERLAY);
-
-            // Set the content view.
-            setContentView(R.layout.bookmarks_databaseview_top_appbar);
-        }
-
-        // Get a handle for the toolbar.
-        Toolbar toolbar = findViewById(R.id.bookmarks_databaseview_toolbar);
-
-        // Set the support action bar.
-        setSupportActionBar(toolbar);
-
-        // Get a handle for the action bar.
-        ActionBar actionBar = getSupportActionBar();
-
-        // Remove the incorrect lint warning that the action bar might be null.
-        assert actionBar != null;
-
-        // Display the spinner and the back arrow in the action bar.
-        actionBar.setCustomView(R.layout.spinner);
-        actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_HOME_AS_UP);
-
-        // Control what the system back command does.
-        OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(true) {
-            @Override
-            public void handleOnBackPressed() {
-                // Prepare to finish the activity.
-                prepareFinish();
-            }
-        };
-
-        // Register the on back pressed callback.
-        getOnBackPressedDispatcher().addCallback(this, onBackPressedCallback);
-
-        // Initialize the database handler.
-        bookmarksDatabaseHelper = new BookmarksDatabaseHelper(this);
-
-        // Create a matrix cursor column name string array.
-        String[] matrixCursorColumnNames = {BookmarksDatabaseHelper.ID, BookmarksDatabaseHelper.BOOKMARK_NAME};
-
-        // Create the matrix cursor in a try-with-resources block so it is correctly closed when finished.
-        try (MatrixCursor matrixCursor = new MatrixCursor(matrixCursorColumnNames)) {
-            // Add "All Folders" and "Home Folder" to the matrix cursor.
-            matrixCursor.addRow(new Object[]{ALL_FOLDERS_DATABASE_ID, getString(R.string.all_folders)});
-            matrixCursor.addRow(new Object[]{HOME_FOLDER_DATABASE_ID, getString(R.string.home_folder)});
-
-            // Get a cursor with the list of all the folders.
-            Cursor foldersCursor = bookmarksDatabaseHelper.getAllFolders();
-
-            // Combine the matrix cursor and the folders cursor.
-            MergeCursor foldersMergeCursor = new MergeCursor(new Cursor[]{matrixCursor, foldersCursor});
-
-
-            // Get the default folder bitmap.  `ContextCompat` must be used until the minimum API >= 21.
-            Drawable defaultFolderDrawable = ContextCompat.getDrawable(getApplicationContext(), R.drawable.folder_blue_bitmap);
-
-            // Cast the default folder drawable to a bitmap drawable.
-            BitmapDrawable defaultFolderBitmapDrawable = (BitmapDrawable) defaultFolderDrawable;
-
-            // Remove the incorrect lint warning that `.getBitmap()` might be null.
-            assert defaultFolderBitmapDrawable != null;
-
-            // Convert the default folder bitmap drawable to a bitmap.
-            Bitmap defaultFolderBitmap = defaultFolderBitmapDrawable.getBitmap();
-
-
-            // Create a resource cursor adapter for the spinner.
-            ResourceCursorAdapter foldersCursorAdapter = new ResourceCursorAdapter(this, R.layout.appbar_spinner_item, foldersMergeCursor, 0) {
-                @Override
-                public void bindView(View view, Context context, Cursor cursor) {
-                    // Get handles for the spinner views.
-                    ImageView spinnerItemImageView = view.findViewById(R.id.spinner_item_imageview);
-                    TextView spinnerItemTextView = view.findViewById(R.id.spinner_item_textview);
-
-                    // Set the folder icon according to the type.
-                    if (foldersMergeCursor.getPosition() > 1) {  // Set a user folder icon.
-                        // Initialize a default folder icon byte array output stream.
-                        ByteArrayOutputStream defaultFolderIconByteArrayOutputStream = new ByteArrayOutputStream();
-
-                        // Covert the default folder bitmap to a PNG and store it in the output stream.  `0` is for lossless compression (the only option for a PNG).
-                        defaultFolderBitmap.compress(Bitmap.CompressFormat.PNG, 0, defaultFolderIconByteArrayOutputStream);
-
-                        // Convert the default folder icon output stream to a byte array.
-                        byte[] defaultFolderIconByteArray = defaultFolderIconByteArrayOutputStream.toByteArray();
-
-
-                        // Get the folder icon byte array from the cursor.
-                        byte[] folderIconByteArray = cursor.getBlob(cursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.FAVORITE_ICON));
-
-                        // Convert the byte array to a bitmap beginning at the first byte and ending at the last.
-                        Bitmap folderIconBitmap = BitmapFactory.decodeByteArray(folderIconByteArray, 0, folderIconByteArray.length);
-
-
-                        // Set the icon according to the type.
-                        if (Arrays.equals(folderIconByteArray, defaultFolderIconByteArray)) {  // The default folder icon is used.
-                            // Set a smaller and darker folder icon, which works well with the spinner.
-                            spinnerItemImageView.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.folder_dark_blue));
-                        } else {  // A custom folder icon is uses.
-                            // Set the folder image stored in the cursor.
-                            spinnerItemImageView.setImageBitmap(folderIconBitmap);
-                        }
-                    } else {  // Set the `All Folders` or `Home Folder` icon.
-                        // Set the gray folder image.  `ContextCompat` must be used until the minimum API >= 21.
-                        spinnerItemImageView.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.folder_gray));
-                    }
-
-                    // Set the text view to display the folder name.
-                    spinnerItemTextView.setText(cursor.getString(cursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_NAME)));
-                }
-            };
-
-            // Set the resource cursor adapter drop drown view resource.
-            foldersCursorAdapter.setDropDownViewResource(R.layout.appbar_spinner_dropdown_item);
-
-            // Get a handle for the folder spinner and set the adapter.
-            Spinner folderSpinner = findViewById(R.id.spinner);
-            folderSpinner.setAdapter(foldersCursorAdapter);
-
-            // Wait to set the on item selected listener until the spinner has been inflated.  Otherwise the activity will crash on restart.
-            folderSpinner.post(() -> {
-                // Handle taps on the spinner dropdown.
-                folderSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
-                    @Override
-                    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
-                        // Store the current folder database ID.
-                        currentFolderDatabaseId = (int) id;
-
-                        // Get a handle for the selected view.
-                        TextView selectedFolderTextView = findViewById(R.id.spinner_item_textview);
-
-                        // Store the current folder name.
-                        currentFolderName = selectedFolderTextView.getText().toString();
-
-                        // Update the list view.
-                        updateBookmarksListView();
-                    }
-
-                    @Override
-                    public void onNothingSelected(AdapterView<?> parent) {
-                        // Do nothing.
-                    }
-                });
-            });
-
-
-            // Get a handle for the bookmarks listview.
-            ListView bookmarksListView = findViewById(R.id.bookmarks_databaseview_listview);
-
-            // Check to see if the activity was restarted.
-            if (savedInstanceState == null) {  // The activity was not restarted.
-                // Set the default current folder database ID.
-                currentFolderDatabaseId = ALL_FOLDERS_DATABASE_ID;
-            } else {  // The activity was restarted.
-                // Restore the class variables from the saved instance state.
-                currentFolderDatabaseId = savedInstanceState.getInt(CURRENT_FOLDER_DATABASE_ID);
-                currentFolderName = savedInstanceState.getString(CURRENT_FOLDER_NAME);
-                sortByDisplayOrder = savedInstanceState.getBoolean(SORT_BY_DISPLAY_ORDER);
-
-                // Update the spinner if the home folder is selected.  Android handles this by default for the main cursor but not the matrix cursor.
-                if (currentFolderDatabaseId == HOME_FOLDER_DATABASE_ID) {
-                    folderSpinner.setSelection(1);
-                }
-            }
-
-            // Update the bookmarks listview.
-            updateBookmarksListView();
-
-            // Setup a `CursorAdapter` with `this` context.  `false` disables autoRequery.
-            bookmarksCursorAdapter = new CursorAdapter(this, bookmarksCursor, false) {
-                @Override
-                public View newView(Context context, Cursor cursor, ViewGroup parent) {
-                    // Inflate the individual item layout.  `false` does not attach it to the root.
-                    return getLayoutInflater().inflate(R.layout.bookmarks_databaseview_item_linearlayout, parent, false);
-                }
-
-                @Override
-                public void bindView(View view, Context context, Cursor cursor) {
-                    boolean isFolder = (cursor.getInt(cursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.IS_FOLDER)) == 1);
-
-                    // Get the database ID from the `Cursor` and display it in `bookmarkDatabaseIdTextView`.
-                    int bookmarkDatabaseId = cursor.getInt(cursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.ID));
-                    TextView bookmarkDatabaseIdTextView = view.findViewById(R.id.bookmarks_databaseview_database_id);
-                    bookmarkDatabaseIdTextView.setText(String.valueOf(bookmarkDatabaseId));
-
-                    // Get the favorite icon byte array from the `Cursor`.
-                    byte[] favoriteIconByteArray = cursor.getBlob(cursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.FAVORITE_ICON));
-                    // Convert the byte array to a `Bitmap` beginning at the beginning at the first byte and ending at the last.
-                    Bitmap favoriteIconBitmap = BitmapFactory.decodeByteArray(favoriteIconByteArray, 0, favoriteIconByteArray.length);
-                    // Display the bitmap in `bookmarkFavoriteIcon`.
-                    ImageView bookmarkFavoriteIcon = view.findViewById(R.id.bookmarks_databaseview_favorite_icon);
-                    bookmarkFavoriteIcon.setImageBitmap(favoriteIconBitmap);
-
-                    // Get the bookmark name from the `Cursor` and display it in `bookmarkNameTextView`.
-                    String bookmarkNameString = cursor.getString(cursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_NAME));
-                    TextView bookmarkNameTextView = view.findViewById(R.id.bookmarks_databaseview_bookmark_name);
-                    bookmarkNameTextView.setText(bookmarkNameString);
-
-                    // Make the font bold for folders.
-                    if (isFolder) {
-                        // The first argument is `null` prevent changing of the font.
-                        bookmarkNameTextView.setTypeface(null, Typeface.BOLD);
-                    } else {  // Reset the font to default.
-                        bookmarkNameTextView.setTypeface(Typeface.DEFAULT);
-                    }
-
-                    // Get the bookmark URL form the `Cursor` and display it in `bookmarkUrlTextView`.
-                    String bookmarkUrlString = cursor.getString(cursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_URL));
-                    TextView bookmarkUrlTextView = view.findViewById(R.id.bookmarks_databaseview_bookmark_url);
-                    bookmarkUrlTextView.setText(bookmarkUrlString);
-
-                    // Hide the URL if the bookmark is a folder.
-                    if (isFolder) {
-                        bookmarkUrlTextView.setVisibility(View.GONE);
-                    } else {
-                        bookmarkUrlTextView.setVisibility(View.VISIBLE);
-                    }
-
-                    // Get the display order from the `Cursor` and display it in `bookmarkDisplayOrderTextView`.
-                    int bookmarkDisplayOrder = cursor.getInt(cursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.DISPLAY_ORDER));
-                    TextView bookmarkDisplayOrderTextView = view.findViewById(R.id.bookmarks_databaseview_display_order);
-                    bookmarkDisplayOrderTextView.setText(String.valueOf(bookmarkDisplayOrder));
-
-                    // Get the parent folder from the `Cursor` and display it in `bookmarkParentFolder`.
-                    String bookmarkParentFolder = cursor.getString(cursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.PARENT_FOLDER));
-                    ImageView parentFolderImageView = view.findViewById(R.id.bookmarks_databaseview_parent_folder_icon);
-                    TextView bookmarkParentFolderTextView = view.findViewById(R.id.bookmarks_databaseview_parent_folder);
-
-                    // Make the folder name gray if it is the home folder.
-                    if (bookmarkParentFolder.isEmpty()) {
-                        parentFolderImageView.setImageDrawable(ContextCompat.getDrawable(getApplicationContext(), R.drawable.folder_gray));
-                        bookmarkParentFolderTextView.setText(R.string.home_folder);
-                        bookmarkParentFolderTextView.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.gray_500));
-                    } else {
-                        parentFolderImageView.setImageDrawable(ContextCompat.getDrawable(getApplicationContext(), R.drawable.folder_dark_blue));
-                        bookmarkParentFolderTextView.setText(bookmarkParentFolder);
-
-                        // Get the current theme status.
-                        int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
-
-                        // Set the text color according to the theme.
-                        if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
-                            // This color is a little darker than the default night mode text.  But the effect is rather nice.
-                            bookmarkParentFolderTextView.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.gray_300));
-                        } else {
-                            bookmarkParentFolderTextView.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.black));
-                        }
-                    }
-                }
-            };
-
-
-            // Update the ListView.
-            bookmarksListView.setAdapter(bookmarksCursorAdapter);
-
-            // Set a listener to edit a bookmark when it is tapped.
-            bookmarksListView.setOnItemClickListener((AdapterView<?> parent, View view, int position, long id) -> {
-                // Convert the database ID to an int.
-                int databaseId = (int) id;
-
-                // Show the edit bookmark or edit bookmark folder dialog.
-                if (bookmarksDatabaseHelper.isFolder(databaseId)) {
-                    // Save the current folder name, which is used in `onSaveBookmarkFolder()`.
-                    oldFolderNameString = bookmarksCursor.getString(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_NAME));
-
-                    // Show the edit bookmark folder dialog.
-                    DialogFragment editBookmarkFolderDatabaseViewDialog = EditBookmarkFolderDatabaseViewDialog.folderDatabaseId(databaseId, favoriteIconBitmap);
-                    editBookmarkFolderDatabaseViewDialog.show(getSupportFragmentManager(), getResources().getString(R.string.edit_folder));
-                } else {
-                    // Show the edit bookmark dialog.
-                    DialogFragment editBookmarkDatabaseViewDialog = EditBookmarkDatabaseViewDialog.bookmarkDatabaseId(databaseId, favoriteIconBitmap);
-                    editBookmarkDatabaseViewDialog.show(getSupportFragmentManager(), getResources().getString(R.string.edit_bookmark));
-                }
-            });
-
-            // Handle long presses on the list view.
-            bookmarksListView.setMultiChoiceModeListener(new AbsListView.MultiChoiceModeListener() {
-                // Instantiate the common variables.
-                MenuItem selectAllMenuItem;
-                MenuItem deleteMenuItem;
-                boolean deletingBookmarks;
-
-                @Override
-                public boolean onCreateActionMode(ActionMode mode, Menu menu) {
-                    // Inflate the menu for the contextual app bar.
-                    getMenuInflater().inflate(R.menu.bookmarks_databaseview_context_menu, menu);
-
-                    // Set the title.
-                    mode.setTitle(R.string.bookmarks);
-
-                    // Get handles for the menu items.
-                    selectAllMenuItem = menu.findItem(R.id.select_all);
-                    deleteMenuItem = menu.findItem(R.id.delete);
-
-                    // Disable the delete menu item if a delete is pending.
-                    deleteMenuItem.setEnabled(!deletingBookmarks);
-
-                    // Get the number of currently selected bookmarks.
-                    int numberOfSelectedBookmarks = bookmarksListView.getCheckedItemCount();
-
-                    // Set the action mode subtitle according to the number of selected bookmarks.  This must be set here or it will be missing if the activity is restarted.
-                    mode.setSubtitle(getString(R.string.selected) + numberOfSelectedBookmarks);
-
-                    // Do not show the select all menu item if all the bookmarks are already checked.
-                    if (bookmarksListView.getCheckedItemCount() == bookmarksListView.getCount()) {
-                        selectAllMenuItem.setVisible(false);
-                    }
-
-                    // Make it so.
-                    return true;
-                }
-
-                @Override
-                public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
-                    // Do nothing.
-                    return false;
-                }
-
-                @Override
-                public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
-                    // Calculate the number of selected bookmarks.
-                    int numberOfSelectedBookmarks = bookmarksListView.getCheckedItemCount();
-
-                    // Only run the commands if at least one bookmark is selected.  Otherwise, a context menu with 0 selected bookmarks is briefly displayed.
-                    if (numberOfSelectedBookmarks > 0) {
-                        // Update the action mode subtitle according to the number of selected bookmarks.
-                        mode.setSubtitle(getString(R.string.selected) + numberOfSelectedBookmarks);
-
-                        // Only show the select all menu item if all of the bookmarks are not already selected.
-                        selectAllMenuItem.setVisible(bookmarksListView.getCheckedItemCount() != bookmarksListView.getCount());
-
-                        // Convert the database ID to an int.
-                        int databaseId = (int) id;
-
-                        // If a folder was selected, also select all the contents.
-                        if (checked && bookmarksDatabaseHelper.isFolder(databaseId)) {
-                            selectAllBookmarksInFolder(databaseId);
-                        }
-
-                        // Do not allow a bookmark to be deselected if the folder is selected.
-                        if (!checked) {
-                            // Get the folder name.
-                            String folderName = bookmarksDatabaseHelper.getParentFolderName((int) id);
-
-                            // If the bookmark is not in the root folder, check to see if the folder is selected.
-                            if (!folderName.isEmpty()) {
-                                // Get the database ID of the folder.
-                                int folderDatabaseId = bookmarksDatabaseHelper.getFolderDatabaseId(folderName);
-
-                                // Move the bookmarks cursor to the first position.
-                                bookmarksCursor.moveToFirst();
-
-                                // Initialize the folder position variable.
-                                int folderPosition = -1;
-
-                                // Get the position of the folder in the bookmarks cursor.
-                                while ((folderPosition < 0) && (bookmarksCursor.getPosition() < bookmarksCursor.getCount())) {
-                                    // Check if the folder database ID matches the bookmark database ID.
-                                    if (folderDatabaseId == bookmarksCursor.getInt(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.ID))) {
-                                        // Get the folder position.
-                                        folderPosition = bookmarksCursor.getPosition();
-
-                                        // Check if the folder is selected.
-                                        if (bookmarksListView.isItemChecked(folderPosition)) {
-                                            // Reselect the bookmark.
-                                            bookmarksListView.setItemChecked(position, true);
-
-                                            // Display a snackbar explaining why the bookmark cannot be deselected.
-                                            Snackbar.make(bookmarksListView, R.string.cannot_deselect_bookmark, Snackbar.LENGTH_LONG).show();
-                                        }
-                                    }
-
-                                    // Increment the bookmarks cursor.
-                                    bookmarksCursor.moveToNext();
-                                }
-                            }
-                        }
-                    }
-                }
-
-                @Override
-                public boolean onActionItemClicked(ActionMode mode, MenuItem menuItem) {
-                    // Get a the menu item ID.
-                    int menuItemId = menuItem.getItemId();
-
-                    // Run the command that corresponds to the selected menu item.
-                    if (menuItemId == R.id.select_all) {  // Select all the bookmarks.
-                        // Get the total number of bookmarks.
-                        int numberOfBookmarks = bookmarksListView.getCount();
-
-                        // Select them all.
-                        for (int i = 0; i < numberOfBookmarks; i++) {
-                            bookmarksListView.setItemChecked(i, true);
-                        }
-                    } else if (menuItemId == R.id.delete) {  // Delete the selected bookmarks.
-                        // Set the deleting bookmarks flag, which prevents the delete menu item from being enabled until the current process finishes.
-                        deletingBookmarks = true;
-
-                        // Get an array of the selected row IDs.
-                        long[] selectedBookmarksIdsLongArray = bookmarksListView.getCheckedItemIds();
-
-                        // Get an array of checked bookmarks.  `.clone()` makes a copy that won't change if the list view is reloaded, which is needed for re-selecting the bookmarks on undelete.
-                        SparseBooleanArray selectedBookmarksPositionsSparseBooleanArray = bookmarksListView.getCheckedItemPositions().clone();
-
-                        // Update the bookmarks cursor with the current contents of the bookmarks database except for the specified database IDs.
-                        switch (currentFolderDatabaseId) {
-                            // Get a cursor with all the folders.
-                            case ALL_FOLDERS_DATABASE_ID:
-                                if (sortByDisplayOrder) {
-                                    bookmarksCursor = bookmarksDatabaseHelper.getAllBookmarksByDisplayOrderExcept(selectedBookmarksIdsLongArray);
-                                } else {
-                                    bookmarksCursor = bookmarksDatabaseHelper.getAllBookmarksExcept(selectedBookmarksIdsLongArray);
-                                }
-                                break;
-
-                            // Get a cursor for the home folder.
-                            case HOME_FOLDER_DATABASE_ID:
-                                if (sortByDisplayOrder) {
-                                    bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrderExcept(selectedBookmarksIdsLongArray, "");
-                                } else {
-                                    bookmarksCursor = bookmarksDatabaseHelper.getBookmarksExcept(selectedBookmarksIdsLongArray, "");
-                                }
-                                break;
-
-                            // Display the selected folder.
-                            default:
-                                // Get a cursor for the selected folder.
-                                if (sortByDisplayOrder) {
-                                    bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrderExcept(selectedBookmarksIdsLongArray, currentFolderName);
-                                } else {
-                                    bookmarksCursor = bookmarksDatabaseHelper.getBookmarksExcept(selectedBookmarksIdsLongArray, currentFolderName);
-                                }
-                        }
-
-                        // Update the list view.
-                        bookmarksCursorAdapter.changeCursor(bookmarksCursor);
-
-                        // Create a Snackbar with the number of deleted bookmarks.
-                        bookmarksDeletedSnackbar = Snackbar.make(findViewById(R.id.bookmarks_databaseview_coordinatorlayout),
-                                        getString(R.string.bookmarks_deleted) + selectedBookmarksIdsLongArray.length, Snackbar.LENGTH_LONG)
-                                .setAction(R.string.undo, view -> {
-                                    // Do nothing because everything will be handled by `onDismissed()` below.
-                                })
-                                .addCallback(new Snackbar.Callback() {
-                                    @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) {
-                                        if (event == Snackbar.Callback.DISMISS_EVENT_ACTION) {  // The user pushed the undo button.
-                                            // Update the bookmarks list view with the current contents of the bookmarks database, including the "deleted bookmarks.
-                                            updateBookmarksListView();
-
-                                            // 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 selected bookmark.
-                                                bookmarksDatabaseHelper.deleteBookmark(databaseIdInt);
-                                            }
-                                        }
-
-                                        // Reset the deleting bookmarks flag.
-                                        deletingBookmarks = false;
-
-                                        // Enable the delete menu item.
-                                        deleteMenuItem.setEnabled(true);
-
-                                        // Close the activity if back has been pressed.
-                                        if (closeActivityAfterDismissingSnackbar) {
-                                            finish();
-                                        }
-                                    }
-                                });
-
-                        // Show the Snackbar.
-                        bookmarksDeletedSnackbar.show();
-                    }
-
-                    // Consume the click.
-                    return false;
-                }
-
-                @Override
-                public void onDestroyActionMode(ActionMode mode) {
-                    // Do nothing.
-                }
-            });
-        }
-    }
-
-    @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
-        // Inflate the menu.
-        getMenuInflater().inflate(R.menu.bookmarks_databaseview_options_menu, menu);
-
-        // Get a handle for the sort menu item.
-        MenuItem sortMenuItem = menu.findItem(R.id.sort);
-
-        // Change the sort menu item icon if the listview is sorted by display order, which restores the state after a restart.
-        if (sortByDisplayOrder) {
-            sortMenuItem.setIcon(R.drawable.sort_selected);
-        }
-
-        // Success.
-        return true;
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem menuItem) {
-        // Get the menu item ID.
-        int menuItemId = menuItem.getItemId();
-
-        // Run the command that corresponds to the selected menu item.
-        if (menuItemId == android.R.id.home) {  // Go Home.  The home arrow is identified as `android.R.id.home`, not just `R.id.home`.
-            // Prepare to finish the activity.
-            prepareFinish();
-        } else if (menuItemId == R.id.sort) {  // Toggle the sort mode.
-            // Update the sort by display order tracker.
-            sortByDisplayOrder = !sortByDisplayOrder;
-
-            // Get a handle for the bookmarks list view.
-            ListView bookmarksListView = findViewById(R.id.bookmarks_databaseview_listview);
-
-            // Update the icon and display a snackbar.
-            if (sortByDisplayOrder) {  // Sort by display order.
-                // Update the icon.
-                menuItem.setIcon(R.drawable.sort_selected);
-
-                // Display a Snackbar indicating the current sort type.
-                Snackbar.make(bookmarksListView, R.string.sorted_by_display_order, Snackbar.LENGTH_SHORT).show();
-            } else {  // Sort by database id.
-                // Update the icon.
-                menuItem.setIcon(R.drawable.sort);
-
-                // Display a Snackbar indicating the current sort type.
-                Snackbar.make(bookmarksListView, R.string.sorted_by_database_id, Snackbar.LENGTH_SHORT).show();
-            }
-
-            // Update the list view.
-            updateBookmarksListView();
-        }
-
-        // Consume the event.
-        return true;
-    }
-
-    @Override
-    public void onSaveInstanceState (@NonNull Bundle savedInstanceState) {
-        // Run the default commands.
-        super.onSaveInstanceState(savedInstanceState);
-
-        // Store the class variables in the bundle.
-        savedInstanceState.putInt(CURRENT_FOLDER_DATABASE_ID, currentFolderDatabaseId);
-        savedInstanceState.putString(CURRENT_FOLDER_NAME, currentFolderName);
-        savedInstanceState.putBoolean(SORT_BY_DISPLAY_ORDER, sortByDisplayOrder);
-    }
-
-    private void prepareFinish() {
-        // Check to see if a snackbar is currently displayed.  If so, it must be closed before existing so that a pending delete is completed before reloading the list view in the bookmarks activity.
-        if ((bookmarksDeletedSnackbar != null) && bookmarksDeletedSnackbar.isShown()) { // Close the bookmarks deleted snackbar before going home.
-            // Set the close flag.
-            closeActivityAfterDismissingSnackbar = true;
-
-            // Dismiss the snackbar.
-            bookmarksDeletedSnackbar.dismiss();
-        } else {  // Go home immediately.
-            // Update the current folder in the bookmarks activity.
-            if ((currentFolderDatabaseId == ALL_FOLDERS_DATABASE_ID) || (currentFolderDatabaseId == HOME_FOLDER_DATABASE_ID)) {  // All folders or the the home folder are currently displayed.
-                // Load the home folder.
-                BookmarksActivity.currentFolder = "";
-            } else {  // A subfolder is currently displayed.
-                // Load the current folder.
-                BookmarksActivity.currentFolder = currentFolderName;
-            }
-
-            // Reload the bookmarks list view when returning to the bookmarks activity.
-            BookmarksActivity.restartFromBookmarksDatabaseViewActivity = true;
-
-            // Exit the bookmarks database view activity.
-            finish();
-        }
-    }
-
-    private void updateBookmarksListView() {
-        // Populate the bookmarks list view based on the spinner selection.
-        switch (currentFolderDatabaseId) {
-            // Get a cursor with all the folders.
-            case ALL_FOLDERS_DATABASE_ID:
-                if (sortByDisplayOrder) {
-                    bookmarksCursor = bookmarksDatabaseHelper.getAllBookmarksByDisplayOrder();
-                } else {
-                    bookmarksCursor = bookmarksDatabaseHelper.getAllBookmarks();
-                }
-                break;
-
-            // Get a cursor for the home folder.
-            case HOME_FOLDER_DATABASE_ID:
-                if (sortByDisplayOrder) {
-                    bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder("");
-                } else {
-                    bookmarksCursor = bookmarksDatabaseHelper.getBookmarks("");
-                }
-                break;
-
-            // Display the selected folder.
-            default:
-                // Get a cursor for the selected folder.
-                if (sortByDisplayOrder) {
-                    bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolderName);
-                } else {
-                    bookmarksCursor = bookmarksDatabaseHelper.getBookmarks(currentFolderName);
-                }
-        }
-
-        // Update the cursor adapter if it isn't null, which happens when the activity is restarted.
-        if (bookmarksCursorAdapter != null) {
-            bookmarksCursorAdapter.changeCursor(bookmarksCursor);
-        }
-    }
-
-    private void selectAllBookmarksInFolder(int folderId) {
-        // Get a handle for the bookmarks list view.
-        ListView bookmarksListView = findViewById(R.id.bookmarks_databaseview_listview);
-
-        // Get the folder name.
-        String folderName = bookmarksDatabaseHelper.getFolderName(folderId);
-
-        // Get a cursor with the contents of the folder.
-        Cursor folderCursor = bookmarksDatabaseHelper.getBookmarks(folderName);
-
-        // Move to the beginning of the cursor.
-        folderCursor.moveToFirst();
-
-        while (folderCursor.getPosition() < folderCursor.getCount()) {
-            // Get the bookmark database ID.
-            int bookmarkId = folderCursor.getInt(folderCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.ID));
-
-            // Move the bookmarks cursor to the first position.
-            bookmarksCursor.moveToFirst();
-
-            // Initialize the bookmark position variable.
-            int bookmarkPosition = -1;
-
-            // Get the position of this bookmark in the bookmarks cursor.
-            while ((bookmarkPosition < 0) && (bookmarksCursor.getPosition() < bookmarksCursor.getCount())) {
-                // Check if the bookmark IDs match.
-                if (bookmarkId == bookmarksCursor.getInt(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.ID))) {
-                    // Get the bookmark position.
-                    bookmarkPosition = bookmarksCursor.getPosition();
-
-                    // If this bookmark is a folder, select all the bookmarks inside it.
-                    if (bookmarksDatabaseHelper.isFolder(bookmarkId)) {
-                        selectAllBookmarksInFolder(bookmarkId);
-                    }
-
-                    // Select the bookmark.
-                    bookmarksListView.setItemChecked(bookmarkPosition, true);
-                }
-
-                // Increment the bookmarks cursor position.
-                bookmarksCursor.moveToNext();
-            }
-
-            // Move to the next position.
-            folderCursor.moveToNext();
-        }
-    }
-
-    @Override
-    public void onSaveBookmark(DialogFragment dialogFragment, int selectedBookmarkDatabaseId, @NonNull Bitmap favoriteIconBitmap) {
-        // Get the dialog from the dialog fragment.
-        Dialog dialog = dialogFragment.getDialog();
-
-        // Remove the incorrect lint warning below that the dialog might be null.
-        assert dialog != null;
-
-        // Get handles for the views from dialog fragment.
-        RadioButton currentIconRadioButton = dialog.findViewById(R.id.current_icon_radiobutton);
-        EditText bookmarkNameEditText = dialog.findViewById(R.id.bookmark_name_edittext);
-        EditText bookmarkUrlEditText = dialog.findViewById(R.id.bookmark_url_edittext);
-        Spinner folderSpinner = dialog.findViewById(R.id.bookmark_folder_spinner);
-        EditText displayOrderEditText = dialog.findViewById(R.id.bookmark_display_order_edittext);
-
-        // Extract the bookmark information.
-        String bookmarkNameString = bookmarkNameEditText.getText().toString();
-        String bookmarkUrlString = bookmarkUrlEditText.getText().toString();
-        int folderDatabaseId = (int) folderSpinner.getSelectedItemId();
-        int displayOrderInt = Integer.parseInt(displayOrderEditText.getText().toString());
-
-        // Instantiate the parent folder name `String`.
-        String parentFolderNameString;
-
-        // Set the parent folder name.
-        if (folderDatabaseId == HOME_FOLDER_DATABASE_ID) {  // The home folder is selected.  Use `""`.
-            parentFolderNameString = "";
-        } else {  // Get the parent folder name from the database.
-            parentFolderNameString = bookmarksDatabaseHelper.getFolderName(folderDatabaseId);
-        }
-
-        // Update the bookmark.
-        if (currentIconRadioButton.isChecked()) {  // Update the bookmark without changing the favorite icon.
-            bookmarksDatabaseHelper.updateBookmark(selectedBookmarkDatabaseId, bookmarkNameString, bookmarkUrlString, parentFolderNameString, displayOrderInt);
-        } else {  // Update the bookmark using the `WebView` favorite icon.
-            // Create a favorite icon byte array output stream.
-            ByteArrayOutputStream newFavoriteIconByteArrayOutputStream = new ByteArrayOutputStream();
-
-            // Convert the favorite icon bitmap to a byte array.  `0` is for lossless compression (the only option for a PNG).
-            favoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, newFavoriteIconByteArrayOutputStream);
-
-            // Convert the favorite icon byte array stream to a byte array.
-            byte[] newFavoriteIconByteArray = newFavoriteIconByteArrayOutputStream.toByteArray();
-
-            //  Update the bookmark and the favorite icon.
-            bookmarksDatabaseHelper.updateBookmark(selectedBookmarkDatabaseId, bookmarkNameString, bookmarkUrlString, parentFolderNameString, displayOrderInt, newFavoriteIconByteArray);
-        }
-
-        // Update the list view.
-        updateBookmarksListView();
-    }
-
-    @Override
-    public void onSaveBookmarkFolder(DialogFragment dialogFragment, int selectedBookmarkDatabaseId, @NonNull Bitmap favoriteIconBitmap) {
-        // Get the dialog from the dialog fragment.
-        Dialog dialog = dialogFragment.getDialog();
-
-        // Remove the incorrect lint warning below that the dialog might be null.
-        assert dialog != null;
-
-        // Get handles for the views from dialog fragment.
-        RadioButton currentIconRadioButton = dialog.findViewById(R.id.current_icon_radiobutton);
-        RadioButton defaultIconRadioButton = dialog.findViewById(R.id.default_icon_radiobutton);
-        ImageView defaultIconImageView = dialog.findViewById(R.id.default_icon_imageview);
-        EditText folderNameEditText = dialog.findViewById(R.id.folder_name_edittext);
-        Spinner parentFolderSpinner = dialog.findViewById(R.id.parent_folder_spinner);
-        EditText displayOrderEditText = dialog.findViewById(R.id.display_order_edittext);
-
-        // Extract the folder information.
-        String newFolderNameString = folderNameEditText.getText().toString();
-        int parentFolderDatabaseId = (int) parentFolderSpinner.getSelectedItemId();
-        int displayOrderInt = Integer.parseInt(displayOrderEditText.getText().toString());
-
-        // Instantiate the parent folder name `String`.
-        String parentFolderNameString;
-
-        // Set the parent folder name.
-        if (parentFolderDatabaseId == HOME_FOLDER_DATABASE_ID) {  // The home folder is selected.  Use `""`.
-            parentFolderNameString = "";
-        } else {  // Get the parent folder name from the database.
-            parentFolderNameString = bookmarksDatabaseHelper.getFolderName(parentFolderDatabaseId);
-        }
-
-        // Update the folder.
-        if (currentIconRadioButton.isChecked()) {  // Update the folder without changing the favorite icon.
-            bookmarksDatabaseHelper.updateFolder(selectedBookmarkDatabaseId, oldFolderNameString, newFolderNameString, parentFolderNameString, displayOrderInt);
-        } else {  // Update the folder and the icon.
-            // Create the new folder icon Bitmap.
-            Bitmap folderIconBitmap;
-
-            // Populate the new folder icon bitmap.
-            if (defaultIconRadioButton.isChecked()) {
-                // Get the default folder icon drawable.
-                Drawable folderIconDrawable = defaultIconImageView.getDrawable();
-
-                // Convert the folder icon drawable to a bitmap drawable.
-                BitmapDrawable folderIconBitmapDrawable = (BitmapDrawable) folderIconDrawable;
-
-                // Convert the folder icon bitmap drawable to a bitmap.
-                folderIconBitmap = folderIconBitmapDrawable.getBitmap();
-            } else {  // Use the `WebView` favorite icon.
-                // Get a copy of the favorite icon bitmap.
-                folderIconBitmap = favoriteIconBitmap;
-            }
-
-            // Create a folder icon byte array output stream.
-            ByteArrayOutputStream newFolderIconByteArrayOutputStream = new ByteArrayOutputStream();
-
-            // Convert the folder icon bitmap to a byte array.  `0` is for lossless compression (the only option for a PNG).
-            folderIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, newFolderIconByteArrayOutputStream);
-
-            // Convert the folder icon byte array stream to a byte array.
-            byte[] newFolderIconByteArray = newFolderIconByteArrayOutputStream.toByteArray();
-
-            //  Update the folder and the icon.
-            bookmarksDatabaseHelper.updateFolder(selectedBookmarkDatabaseId, oldFolderNameString, newFolderNameString, parentFolderNameString, displayOrderInt, newFolderIconByteArray);
-        }
-
-        // Update the list view.
-        updateBookmarksListView();
-    }
-
-    @Override
-    public void onDestroy() {
-        // Close the bookmarks cursor and database.
-        bookmarksCursor.close();
-        bookmarksDatabaseHelper.close();
-
-        // Run the default commands.
-        super.onDestroy();
-    }
-}
diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksDatabaseViewActivity.kt b/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksDatabaseViewActivity.kt
new file mode 100644 (file)
index 0000000..ad408ec
--- /dev/null
@@ -0,0 +1,852 @@
+/*
+ * Copyright 2016-2023 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
+ *
+ * Privacy Browser Android 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 Android 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 Android.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.stoutner.privacybrowser.activities
+
+import android.content.Context
+import android.database.Cursor
+import android.database.MatrixCursor
+import android.database.MergeCursor
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.graphics.Typeface
+import android.graphics.drawable.BitmapDrawable
+import android.os.Bundle
+import android.view.ActionMode
+import android.view.Menu
+import android.view.MenuItem
+import android.view.View
+import android.view.ViewGroup
+import android.view.Window
+import android.view.WindowManager
+import android.widget.AbsListView.MultiChoiceModeListener
+import android.widget.AdapterView
+import android.widget.EditText
+import android.widget.ImageView
+import android.widget.ListView
+import android.widget.RadioButton
+import android.widget.Spinner
+import android.widget.TextView
+
+import androidx.activity.OnBackPressedCallback
+import androidx.appcompat.app.ActionBar
+import androidx.appcompat.app.AppCompatActivity
+import androidx.appcompat.content.res.AppCompatResources
+import androidx.appcompat.widget.Toolbar
+import androidx.cursoradapter.widget.CursorAdapter
+import androidx.cursoradapter.widget.ResourceCursorAdapter
+import androidx.fragment.app.DialogFragment
+import androidx.preference.PreferenceManager
+
+import com.google.android.material.snackbar.Snackbar
+
+import com.stoutner.privacybrowser.R
+import com.stoutner.privacybrowser.dialogs.EditBookmarkDatabaseViewDialog.Companion.bookmarkDatabaseId
+import com.stoutner.privacybrowser.dialogs.EditBookmarkDatabaseViewDialog.EditBookmarkDatabaseViewListener
+import com.stoutner.privacybrowser.dialogs.EditBookmarkFolderDatabaseViewDialog.Companion.folderDatabaseId
+import com.stoutner.privacybrowser.dialogs.EditBookmarkFolderDatabaseViewDialog.EditBookmarkFolderDatabaseViewListener
+import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper
+
+import java.io.ByteArrayOutputStream
+
+import java.util.Arrays
+
+// Define the private class constants.
+private const val ALL_FOLDERS_DATABASE_ID = -2
+private const val CURRENT_FOLDER_DATABASE_ID = "current_folder_database_id"
+private const val CURRENT_FOLDER_NAME = "current_folder_name"
+private const val SORT_BY_DISPLAY_ORDER = "sort_by_display_order"
+
+class BookmarksDatabaseViewActivity : AppCompatActivity(), EditBookmarkDatabaseViewListener, EditBookmarkFolderDatabaseViewListener {
+    companion object {
+        // Define the public class constants.
+        const val HOME_FOLDER_DATABASE_ID = -1
+    }
+
+    // Define the class variables.
+    private var closeActivityAfterDismissingSnackbar = false
+    private var currentFolderDatabaseId = 0
+    private var bookmarksCursorAdapter: CursorAdapter? = null
+    private var bookmarksDeletedSnackbar: Snackbar? = null
+    private var sortByDisplayOrder = false
+
+    // Declare the class variables.
+    private lateinit var bookmarksCursor: Cursor
+    private lateinit var bookmarksDatabaseHelper: BookmarksDatabaseHelper
+    private lateinit var bookmarksListView: ListView
+    private lateinit var currentFolderName: String
+    private lateinit var oldFolderNameString: String
+
+    public override fun onCreate(savedInstanceState: Bundle?) {
+        // Get a handle for the shared preferences.
+        val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
+
+        // Get the preferences.
+        val allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false)
+        val bottomAppBar = sharedPreferences.getBoolean(getString(R.string.bottom_app_bar_key), false)
+
+        // Disable screenshots if not allowed.
+        if (!allowScreenshots) {
+            window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
+        }
+
+        // Run the default commands.
+        super.onCreate(savedInstanceState)
+
+        // Get the favorite icon byte array.
+        val favoriteIconByteArray = intent.getByteArrayExtra(CURRENT_FAVORITE_ICON_BYTE_ARRAY)!!
+
+        // Convert the favorite icon byte array to a bitmap and store it in a class variable.
+        val favoriteIconBitmap = BitmapFactory.decodeByteArray(favoriteIconByteArray, 0, favoriteIconByteArray.size)
+
+        // Set the view according to the theme.
+        if (bottomAppBar) {
+            // Set the content view.
+            setContentView(R.layout.bookmarks_databaseview_bottom_appbar)
+        } else {
+            // `Window.FEATURE_ACTION_MODE_OVERLAY` makes the contextual action mode cover the support action bar.  It must be requested before the content is set.
+            supportRequestWindowFeature(Window.FEATURE_ACTION_MODE_OVERLAY)
+
+            // Set the content view.
+            setContentView(R.layout.bookmarks_databaseview_top_appbar)
+        }
+
+        // Get a handle for the toolbar.
+        val toolbar = findViewById<Toolbar>(R.id.bookmarks_databaseview_toolbar)
+        val bookmarksListView = findViewById<ListView>(R.id.bookmarks_databaseview_listview)
+
+        // Set the support action bar.
+        setSupportActionBar(toolbar)
+
+        // Get a handle for the app bar.
+        val appBar = supportActionBar!!
+
+        // Set the app bar custom view.
+        appBar.setCustomView(R.layout.spinner)
+
+        // Display the back arrow in the app bar.
+        appBar.displayOptions = ActionBar.DISPLAY_SHOW_CUSTOM or ActionBar.DISPLAY_HOME_AS_UP
+
+        // Control what the system back command does.
+        val onBackPressedCallback: OnBackPressedCallback = object : OnBackPressedCallback(true) {
+            override fun handleOnBackPressed() {
+                // Prepare to finish the activity.
+                prepareFinish()
+            }
+        }
+
+        // Register the on back pressed callback.
+        onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
+
+        // Initialize the database handler.
+        bookmarksDatabaseHelper = BookmarksDatabaseHelper(this)
+
+        // Create a matrix cursor column name string array.
+        val matrixCursorColumnNames = arrayOf(BookmarksDatabaseHelper.ID, BookmarksDatabaseHelper.BOOKMARK_NAME)
+
+        // Setup the matrix cursor.
+        MatrixCursor(matrixCursorColumnNames).use { matrixCursor ->
+            // Add "All Folders" and "Home Folder" to the matrix cursor.
+            matrixCursor.addRow(arrayOf<Any>(ALL_FOLDERS_DATABASE_ID, getString(R.string.all_folders)))
+            matrixCursor.addRow(arrayOf<Any>(HOME_FOLDER_DATABASE_ID, getString(R.string.home_folder)))
+
+            // Get a cursor with the list of all the folders.
+            val foldersCursor = bookmarksDatabaseHelper.allFolders
+
+            // Combine the matrix cursor and the folders cursor.
+            val foldersMergeCursor = MergeCursor(arrayOf(matrixCursor, foldersCursor))
+
+            // Get the default folder bitmap.
+            val defaultFolderDrawable = AppCompatResources.getDrawable(this, R.drawable.folder_blue_bitmap)
+
+            // Cast the default folder drawable to a bitmap drawable.
+            val defaultFolderBitmapDrawable = (defaultFolderDrawable as BitmapDrawable)
+
+            // Convert the default folder bitmap drawable to a bitmap.
+            val defaultFolderBitmap = defaultFolderBitmapDrawable.bitmap
+
+            // Create a resource cursor adapter for the spinner.
+            val foldersCursorAdapter: ResourceCursorAdapter = object : ResourceCursorAdapter(this, R.layout.appbar_spinner_item, foldersMergeCursor, 0) {
+                override fun bindView(view: View, context: Context, cursor: Cursor) {
+                    // Get handles for the spinner views.
+                    val spinnerItemImageView = view.findViewById<ImageView>(R.id.spinner_item_imageview)
+                    val spinnerItemTextView = view.findViewById<TextView>(R.id.spinner_item_textview)
+
+                    // Set the folder icon according to the type.
+                    if (foldersMergeCursor.position > 1) {  // Set a user folder icon.
+                        // Initialize a default folder icon byte array output stream.
+                        val defaultFolderIconByteArrayOutputStream = ByteArrayOutputStream()
+
+                        // Covert the default folder bitmap to a PNG and store it in the output stream.  `0` is for lossless compression (the only option for a PNG).
+                        defaultFolderBitmap.compress(Bitmap.CompressFormat.PNG, 0, defaultFolderIconByteArrayOutputStream)
+
+                        // Convert the default folder icon output stream to a byte array.
+                        val defaultFolderIconByteArray = defaultFolderIconByteArrayOutputStream.toByteArray()
+
+                        // Get the folder icon byte array from the cursor.
+                        val folderIconByteArray = cursor.getBlob(cursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.FAVORITE_ICON))
+
+                        // Convert the byte array to a bitmap beginning at the first byte and ending at the last.
+                        val folderIconBitmap = BitmapFactory.decodeByteArray(folderIconByteArray, 0, folderIconByteArray.size)
+
+                        // Set the icon according to the type.
+                        if (Arrays.equals(folderIconByteArray, defaultFolderIconByteArray)) {  // The default folder icon is used.
+                            // Set a smaller and darker folder icon, which works well with the spinner.
+                            spinnerItemImageView.setImageDrawable(AppCompatResources.getDrawable(context, R.drawable.folder_dark_blue))
+                        } else {  // A custom folder icon is uses.
+                            // Set the folder image stored in the cursor.
+                            spinnerItemImageView.setImageBitmap(folderIconBitmap)
+                        }
+                    } else {  // Set the `All Folders` or `Home Folder` icon.
+                        // Set the gray folder image.
+                        spinnerItemImageView.setImageDrawable(AppCompatResources.getDrawable(context, R.drawable.folder_gray))
+                    }
+
+                    // Set the text view to display the folder name.
+                    spinnerItemTextView.text = cursor.getString(cursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_NAME))
+                }
+            }
+
+            // Set the resource cursor adapter drop drown view resource.
+            foldersCursorAdapter.setDropDownViewResource(R.layout.appbar_spinner_dropdown_item)
+
+            // Get a handle for the folder spinner.
+            val folderSpinner = findViewById<Spinner>(R.id.spinner)
+
+            // Set the folder spinner adapter.
+            folderSpinner.adapter = foldersCursorAdapter
+
+            // Wait to set the on item selected listener until the spinner has been inflated.  Otherwise the activity will crash on restart.
+            folderSpinner.post {
+                // Handle taps on the spinner dropdown.
+                folderSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
+                    override fun onItemSelected(parent: AdapterView<*>?, view: View, position: Int, id: Long) {
+                        // Store the current folder database ID.
+                        currentFolderDatabaseId = id.toInt()
+
+                        // Get a handle for the selected view.
+                        val selectedFolderTextView = findViewById<TextView>(R.id.spinner_item_textview)
+
+                        // Store the current folder name.
+                        currentFolderName = selectedFolderTextView.text.toString()
+
+                        // Update the list view.
+                        updateBookmarksListView()
+                    }
+
+                    override fun onNothingSelected(parent: AdapterView<*>?) {
+                        // Do nothing.
+                    }
+                }
+            }
+
+            // Check to see if the activity was restarted.
+            if (savedInstanceState == null) {  // The activity was not restarted.
+                // Set the default current folder database ID.
+                currentFolderDatabaseId = ALL_FOLDERS_DATABASE_ID
+            } else {  // The activity was restarted.
+                // Restore the class variables from the saved instance state.
+                currentFolderDatabaseId = savedInstanceState.getInt(CURRENT_FOLDER_DATABASE_ID)
+                currentFolderName = savedInstanceState.getString(CURRENT_FOLDER_NAME)!!
+                sortByDisplayOrder = savedInstanceState.getBoolean(SORT_BY_DISPLAY_ORDER)
+
+                // Update the spinner if the home folder is selected.  Android handles this by default for the main cursor but not the matrix cursor.
+                if (currentFolderDatabaseId == HOME_FOLDER_DATABASE_ID) {
+                    folderSpinner.setSelection(1)
+                }
+            }
+
+            // Update the bookmarks listview.
+            updateBookmarksListView()
+
+            // Setup a cursor adapter.
+            bookmarksCursorAdapter = object : CursorAdapter(this, bookmarksCursor, false) {
+                override fun newView(context: Context, cursor: Cursor, parent: ViewGroup): View {
+                    // Inflate the individual item layout.  `false` does not attach it to the root.
+                    return layoutInflater.inflate(R.layout.bookmarks_databaseview_item_linearlayout, parent, false)
+                }
+
+                override fun bindView(view: View, context: Context, cursor: Cursor) {
+                    // Get handles for the views.
+                    val bookmarkDatabaseIdTextView = view.findViewById<TextView>(R.id.bookmarks_databaseview_database_id)
+                    val bookmarkFavoriteIcon = view.findViewById<ImageView>(R.id.bookmarks_databaseview_favorite_icon)
+                    val bookmarkNameTextView = view.findViewById<TextView>(R.id.bookmarks_databaseview_bookmark_name)
+                    val bookmarkUrlTextView = view.findViewById<TextView>(R.id.bookmarks_databaseview_bookmark_url)
+                    val bookmarkDisplayOrderTextView = view.findViewById<TextView>(R.id.bookmarks_databaseview_display_order)
+                    val parentFolderImageView = view.findViewById<ImageView>(R.id.bookmarks_databaseview_parent_folder_icon)
+                    val bookmarkParentFolderTextView = view.findViewById<TextView>(R.id.bookmarks_databaseview_parent_folder)
+
+                    // Get the information from the cursor.
+                    val bookmarkDatabaseId = cursor.getInt(cursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.ID))
+                    val bookmarkFavoriteIconByteArray = cursor.getBlob(cursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.FAVORITE_ICON))
+                    val bookmarkNameString = cursor.getString(cursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_NAME))
+                    val bookmarkUrlString = cursor.getString(cursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_URL))
+                    val bookmarkDisplayOrder = cursor.getInt(cursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.DISPLAY_ORDER))
+                    val bookmarkParentFolder = cursor.getString(cursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.PARENT_FOLDER))
+
+                    // Convert the byte array to a `Bitmap` beginning at the beginning at the first byte and ending at the last.
+                    val bookmarkFavoriteIconBitmap = BitmapFactory.decodeByteArray(bookmarkFavoriteIconByteArray, 0, bookmarkFavoriteIconByteArray.size)
+
+                    // Populate the views.
+                    bookmarkDatabaseIdTextView.text = bookmarkDatabaseId.toString()
+                    bookmarkFavoriteIcon.setImageBitmap(bookmarkFavoriteIconBitmap)
+                    bookmarkNameTextView.text = bookmarkNameString
+                    bookmarkUrlTextView.text = bookmarkUrlString
+                    bookmarkDisplayOrderTextView.text = bookmarkDisplayOrder.toString()
+
+                    // Check to see if the bookmark is a folder.
+                    if (cursor.getInt(cursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.IS_FOLDER)) == 1) {  // The bookmark is a folder.
+                        // Make the font bold.  When the first argument is null the font is not changed.
+                        bookmarkNameTextView.setTypeface(null, Typeface.BOLD)
+
+                        // Hide the URL.
+                        bookmarkUrlTextView.visibility = View.GONE
+                    } else {  // The bookmark is not a folder.
+                        // Reset the font to default.
+                        bookmarkNameTextView.typeface = Typeface.DEFAULT
+
+                        // Show the URL.
+                        bookmarkUrlTextView.visibility = View.VISIBLE
+                    }
+
+                    // Make the folder name gray if it is the home folder.
+                    if (bookmarkParentFolder.isEmpty()) {  // The bookmark is in the home folder.
+                        // Get the home folder icon.
+                        parentFolderImageView.setImageDrawable(AppCompatResources.getDrawable(applicationContext, R.drawable.folder_gray))
+
+                        // Set the parent folder text to be `Home Folder`.
+                        bookmarkParentFolderTextView.setText(R.string.home_folder)
+
+                        // Set the home folder text to be gray.
+                        bookmarkParentFolderTextView.setTextColor(getColor(R.color.gray_500))
+                    } else {  // The bookmark is in a subfolder.
+                        // Get the parent folder icon.
+                        parentFolderImageView.setImageDrawable(AppCompatResources.getDrawable(applicationContext, R.drawable.folder_dark_blue))
+
+                        // Set the parent folder name.
+                        bookmarkParentFolderTextView.text = bookmarkParentFolder
+
+                        // Set the parent folder text color.
+                        bookmarkParentFolderTextView.setTextColor(getColor(R.color.parent_folder_text))
+                    }
+                }
+            }
+
+            // Update the ListView.
+            bookmarksListView.adapter = bookmarksCursorAdapter
+
+            // Set a listener to edit a bookmark when it is tapped.
+            bookmarksListView.onItemClickListener = AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, _: Int, id: Long ->
+                // Convert the database ID to an int.
+                val databaseId = id.toInt()
+
+                // Show the edit bookmark or edit bookmark folder dialog.
+                if (bookmarksDatabaseHelper.isFolder(databaseId)) {
+                    // Save the current folder name, which is used in `onSaveBookmarkFolder()`.
+                    oldFolderNameString = bookmarksCursor.getString(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_NAME))
+
+                    // Instantiate the edit bookmark folder dialog.
+                    val editBookmarkFolderDatabaseViewDialog: DialogFragment = folderDatabaseId(databaseId, favoriteIconBitmap)
+
+                    // Make it so.
+                    editBookmarkFolderDatabaseViewDialog.show(supportFragmentManager, resources.getString(R.string.edit_folder))
+                } else {
+                    // Instantiate the edit bookmark dialog.
+                    val editBookmarkDatabaseViewDialog: DialogFragment = bookmarkDatabaseId(databaseId, favoriteIconBitmap)
+
+                    // Make it so.
+                    editBookmarkDatabaseViewDialog.show(supportFragmentManager, resources.getString(R.string.edit_bookmark))
+                }
+            }
+
+            // Handle long presses on the list view.
+            bookmarksListView.setMultiChoiceModeListener(object : MultiChoiceModeListener {
+                // Instantiate the common variables.
+                private lateinit var selectAllMenuItem: MenuItem
+                private lateinit var deleteMenuItem: MenuItem
+                private var deletingBookmarks = false
+
+                override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
+                    // Inflate the menu for the contextual app bar.
+                    menuInflater.inflate(R.menu.bookmarks_databaseview_context_menu, menu)
+
+                    // Get handles for the menu items.
+                    selectAllMenuItem = menu.findItem(R.id.select_all)
+                    deleteMenuItem = menu.findItem(R.id.delete)
+
+                    // Disable the delete menu item if a delete is pending.
+                    deleteMenuItem.isEnabled = !deletingBookmarks
+
+                    // Get the number of currently selected bookmarks.
+                    val numberOfSelectedBookmarks = bookmarksListView.checkedItemCount
+
+                    // Set the title.
+                    mode.setTitle(R.string.bookmarks)
+
+                    // Set the action mode subtitle according to the number of selected bookmarks.  This must be set here or it will be missing if the activity is restarted.
+                    mode.subtitle = getString(R.string.selected, numberOfSelectedBookmarks)
+
+                    // Do not show the select all menu item if all the bookmarks are already checked.
+                    if (numberOfSelectedBookmarks == bookmarksListView.count)
+                        selectAllMenuItem.isVisible = false
+
+                    // Make it so.
+                    return true
+                }
+
+                override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
+                    // Do nothing.
+                    return false
+                }
+
+                override fun onItemCheckedStateChanged(mode: ActionMode, position: Int, id: Long, checked: Boolean) {
+                    // Calculate the number of selected bookmarks.
+                    val numberOfSelectedBookmarks = bookmarksListView.checkedItemCount
+
+                    // Only run the commands if at least one bookmark is selected.  Otherwise, a context menu with 0 selected bookmarks is briefly displayed.
+                    if (numberOfSelectedBookmarks > 0) {
+                        // Update the action mode subtitle according to the number of selected bookmarks.
+                        mode.subtitle = getString(R.string.selected, numberOfSelectedBookmarks)
+
+                        // Only show the select all menu item if all of the bookmarks are not already selected.
+                        selectAllMenuItem.isVisible = (bookmarksListView.checkedItemCount != bookmarksListView.count)
+
+                        // Convert the database ID to an int.
+                        val databaseId = id.toInt()
+
+                        // If a folder was selected, also select all the contents.
+                        if (checked && bookmarksDatabaseHelper.isFolder(databaseId))
+                            selectAllBookmarksInFolder(databaseId)
+
+                        // Do not allow a bookmark to be deselected if the folder is selected.
+                        if (!checked) {
+                            // Get the folder name.
+                            val folderName = bookmarksDatabaseHelper.getParentFolderName(id.toInt())
+
+                            // If the bookmark is not in the root folder, check to see if the folder is selected.
+                            if (folderName.isNotEmpty()) {
+                                // Get the database ID of the folder.
+                                val folderDatabaseId = bookmarksDatabaseHelper.getFolderDatabaseId(folderName)
+
+                                // Move the bookmarks cursor to the first position.
+                                bookmarksCursor.moveToFirst()
+
+                                // Initialize the folder position variable.
+                                var folderPosition = -1
+
+                                // Get the position of the folder in the bookmarks cursor.
+                                while ((folderPosition < 0) && (bookmarksCursor.position < bookmarksCursor.count)) {
+                                    // Check if the folder database ID matches the bookmark database ID.
+                                    if (folderDatabaseId == bookmarksCursor.getInt(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.ID))) {
+                                        // Get the folder position.
+                                        folderPosition = bookmarksCursor.position
+
+                                        // Check if the folder is selected.
+                                        if (bookmarksListView.isItemChecked(folderPosition)) {
+                                            // Reselect the bookmark.
+                                            bookmarksListView.setItemChecked(position, true)
+
+                                            // Display a snackbar explaining why the bookmark cannot be deselected.
+                                            Snackbar.make(bookmarksListView, R.string.cannot_deselect_bookmark, Snackbar.LENGTH_LONG).show()
+                                        }
+                                    }
+
+                                    // Increment the bookmarks cursor.
+                                    bookmarksCursor.moveToNext()
+                                }
+                            }
+                        }
+                    }
+                }
+
+                override fun onActionItemClicked(mode: ActionMode, menuItem: MenuItem): Boolean {
+                    // Get a the menu item ID.
+                    val menuItemId = menuItem.itemId
+
+                    // Run the command that corresponds to the selected menu item.
+                    if (menuItemId == R.id.select_all) {  // Select all the bookmarks.
+                        // Get the total number of bookmarks.
+                        val numberOfBookmarks = bookmarksListView.count
+
+                        // Select them all.
+                        for (i in 0 until numberOfBookmarks) {
+                            bookmarksListView.setItemChecked(i, true)
+                        }
+                    } else if (menuItemId == R.id.delete) {  // Delete the selected bookmarks.
+                        // Set the deleting bookmarks flag, which prevents the delete menu item from being enabled until the current process finishes.
+                        deletingBookmarks = true
+
+                        // Get an array of the selected row IDs.
+                        val selectedBookmarksIdsLongArray = bookmarksListView.checkedItemIds
+
+                        // Get an array of checked bookmarks.  `.clone()` makes a copy that won't change if the list view is reloaded, which is needed for re-selecting the bookmarks on undelete.
+                        val selectedBookmarksPositionsSparseBooleanArray = bookmarksListView.checkedItemPositions.clone()
+
+                        // Populate the bookmarks cursor.
+                        bookmarksCursor = when (currentFolderDatabaseId) {
+                            // Get all the bookmarks except the ones being deleted.
+                            ALL_FOLDERS_DATABASE_ID ->
+                                if (sortByDisplayOrder)
+                                    bookmarksDatabaseHelper.getAllBookmarksByDisplayOrderExcept(selectedBookmarksIdsLongArray)
+                                else
+                                    bookmarksDatabaseHelper.getAllBookmarksExcept(selectedBookmarksIdsLongArray)
+
+                            // Get the home folder bookmarks except the ones being deleted.
+                            HOME_FOLDER_DATABASE_ID ->
+                                if (sortByDisplayOrder)
+                                    bookmarksDatabaseHelper.getBookmarksByDisplayOrderExcept(selectedBookmarksIdsLongArray, "")
+                                else
+                                    bookmarksDatabaseHelper.getBookmarksExcept(selectedBookmarksIdsLongArray, "")
+
+
+                            // Get the current folder bookmarks except the ones being deleted.
+                            else ->
+                                if (sortByDisplayOrder)
+                                    bookmarksDatabaseHelper.getBookmarksByDisplayOrderExcept(selectedBookmarksIdsLongArray, currentFolderName)
+                                else
+                                    bookmarksDatabaseHelper.getBookmarksExcept(selectedBookmarksIdsLongArray, currentFolderName)
+                        }
+
+                        // Update the list view.
+                        bookmarksCursorAdapter!!.changeCursor(bookmarksCursor)
+
+                        // Create a snackbar with the number of deleted bookmarks.
+                        bookmarksDeletedSnackbar = Snackbar.make(findViewById(R.id.bookmarks_databaseview_coordinatorlayout), getString(R.string.bookmarks_deleted, selectedBookmarksIdsLongArray.size),
+                            Snackbar.LENGTH_LONG)
+                            .setAction(R.string.undo) {}  // Undo will be handles by `onDismissed()` below.
+                            .addCallback(object : Snackbar.Callback() {
+                                override fun onDismissed(snackbar: Snackbar, event: Int) {
+                                    if (event == DISMISS_EVENT_ACTION) {  // The user pushed the undo button.
+                                        // Update the bookmarks list view with the current contents of the bookmarks database, including the "deleted" bookmarks.
+                                        updateBookmarksListView()
+
+                                        // Re-select the previously selected bookmarks.
+                                        for (i in 0 until selectedBookmarksPositionsSparseBooleanArray.size())
+                                            bookmarksListView.setItemChecked(selectedBookmarksPositionsSparseBooleanArray.keyAt(i), true)
+                                    } else {  // The snackbar was dismissed without the undo button being pushed.
+                                        // Delete each selected bookmark.
+                                        for (databaseIdLong in selectedBookmarksIdsLongArray) {
+                                            // Convert the database long ID to an int.
+                                            val databaseIdInt = databaseIdLong.toInt()
+
+                                            // Delete the selected bookmark.
+                                            bookmarksDatabaseHelper.deleteBookmark(databaseIdInt)
+                                        }
+                                    }
+
+                                    // Reset the deleting bookmarks flag.
+                                    deletingBookmarks = false
+
+                                    // Enable the delete menu item.
+                                    deleteMenuItem.isEnabled = true
+
+                                    // Close the activity if back has been pressed.
+                                    if (closeActivityAfterDismissingSnackbar)
+                                        finish()
+                                }
+                            })
+
+                        // Show the snackbar.
+                        bookmarksDeletedSnackbar!!.show()
+                    }
+
+                    // Consume the click.
+                    return false
+                }
+
+                override fun onDestroyActionMode(mode: ActionMode) {
+                    // Do nothing.
+                }
+            })
+        }
+    }
+
+    override fun onCreateOptionsMenu(menu: Menu): Boolean {
+        // Inflate the menu.
+        menuInflater.inflate(R.menu.bookmarks_databaseview_options_menu, menu)
+
+        // Get a handle for the sort menu item.
+        val sortMenuItem = menu.findItem(R.id.sort)
+
+        // Change the sort menu item icon if the listview is sorted by display order, which restores the state after a restart.
+        if (sortByDisplayOrder)
+            sortMenuItem.setIcon(R.drawable.sort_selected)
+
+        // Success.
+        return true
+    }
+
+    override fun onOptionsItemSelected(menuItem: MenuItem): Boolean {
+        // Get the menu item ID.
+        val menuItemId = menuItem.itemId
+
+        // Run the command that corresponds to the selected menu item.
+        if (menuItemId == android.R.id.home) {  // Go Home.  The home arrow is identified as `android.R.id.home`, not just `R.id.home`.
+            // Prepare to finish the activity.
+            prepareFinish()
+        } else if (menuItemId == R.id.sort) {  // Toggle the sort mode.
+            // Update the sort by display order tracker.
+            sortByDisplayOrder = !sortByDisplayOrder
+
+            // Update the icon and display a snackbar.
+            if (sortByDisplayOrder) {  // Sort by display order.
+                // Update the icon.
+                menuItem.setIcon(R.drawable.sort_selected)
+
+                // Display a snackbar indicating the current sort type.
+                Snackbar.make(bookmarksListView, R.string.sorted_by_display_order, Snackbar.LENGTH_SHORT).show()
+            } else {  // Sort by database id.
+                // Update the icon.
+                menuItem.setIcon(R.drawable.sort)
+
+                // Display a snackbar indicating the current sort type.
+                Snackbar.make(bookmarksListView, R.string.sorted_by_database_id, Snackbar.LENGTH_SHORT).show()
+            }
+
+            // Update the list view.
+            updateBookmarksListView()
+        }
+
+        // Consume the event.
+        return true
+    }
+
+    public override fun onSaveInstanceState(savedInstanceState: Bundle) {
+        // Run the default commands.
+        super.onSaveInstanceState(savedInstanceState)
+
+        // Store the class variables in the bundle.
+        savedInstanceState.putInt(CURRENT_FOLDER_DATABASE_ID, currentFolderDatabaseId)
+        savedInstanceState.putString(CURRENT_FOLDER_NAME, currentFolderName)
+        savedInstanceState.putBoolean(SORT_BY_DISPLAY_ORDER, sortByDisplayOrder)
+    }
+
+    private fun prepareFinish() {
+        // Check to see if a snackbar is currently displayed.  If so, it must be closed before existing so that a pending delete is completed before reloading the list view in the bookmarks activity.
+        if ((bookmarksDeletedSnackbar != null) && bookmarksDeletedSnackbar!!.isShown) { // Close the bookmarks deleted snackbar before going home.
+            // Set the close flag.
+            closeActivityAfterDismissingSnackbar = true
+
+            // Dismiss the snackbar.
+            bookmarksDeletedSnackbar!!.dismiss()
+        } else {  // Go home immediately.
+            // Update the current folder in the bookmarks activity.
+            if (currentFolderDatabaseId == ALL_FOLDERS_DATABASE_ID || currentFolderDatabaseId == HOME_FOLDER_DATABASE_ID) {  // All folders or the the home folder are currently displayed.
+                // Load the home folder.
+                BookmarksActivity.currentFolder = ""
+            } else {  // A subfolder is currently displayed.
+                // Load the current folder.
+                BookmarksActivity.currentFolder = currentFolderName
+            }
+
+            // Reload the bookmarks list view when returning to the bookmarks activity.
+            BookmarksActivity.restartFromBookmarksDatabaseViewActivity = true
+
+            // Exit the bookmarks database view activity.
+            finish()
+        }
+    }
+
+    private fun updateBookmarksListView() {
+        // Populate the bookmarks list view based on the spinner selection.
+        bookmarksCursor = when (currentFolderDatabaseId) {
+            // Get all the bookmarks.
+            ALL_FOLDERS_DATABASE_ID ->
+                if (sortByDisplayOrder)
+                    bookmarksDatabaseHelper.allBookmarksByDisplayOrder
+                else
+                    bookmarksDatabaseHelper.allBookmarks
+
+            // Get the bookmarks in the current folder.
+            HOME_FOLDER_DATABASE_ID ->
+                if (sortByDisplayOrder)
+                    bookmarksDatabaseHelper.getBookmarksByDisplayOrder("")
+                else
+                    bookmarksDatabaseHelper.getBookmarks("")
+
+            // Get the bookmarks in the specified folder.
+            else ->
+                if (sortByDisplayOrder)
+                    bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolderName)
+                else
+                    bookmarksDatabaseHelper.getBookmarks(currentFolderName)
+        }
+
+        // Update the cursor adapter if it isn't null, which happens when the activity is restarted.
+        if (bookmarksCursorAdapter != null) {
+            bookmarksCursorAdapter!!.changeCursor(bookmarksCursor)
+        }
+    }
+
+    private fun selectAllBookmarksInFolder(folderId: Int) {
+        // Get the folder name.
+        val folderName = bookmarksDatabaseHelper.getFolderName(folderId)
+
+        // Get a cursor with the contents of the folder.
+        val folderCursor = bookmarksDatabaseHelper.getBookmarks(folderName)
+
+        // Move to the beginning of the cursor.
+        folderCursor.moveToFirst()
+
+        while (folderCursor.position < folderCursor.count) {
+            // Get the bookmark database ID.
+            val bookmarkId = folderCursor.getInt(folderCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.ID))
+
+            // Move the bookmarks cursor to the first position.
+            bookmarksCursor.moveToFirst()
+
+            // Initialize the bookmark position variable.
+            var bookmarkPosition = -1
+
+            // Get the position of this bookmark in the bookmarks cursor.
+            while ((bookmarkPosition < 0) && (bookmarksCursor.position < bookmarksCursor.count)) {
+                // Check if the bookmark IDs match.
+                if (bookmarkId == bookmarksCursor.getInt(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.ID))) {
+                    // Get the bookmark position.
+                    bookmarkPosition = bookmarksCursor.position
+
+                    // If this bookmark is a folder, select all the bookmarks inside it.
+                    if (bookmarksDatabaseHelper.isFolder(bookmarkId))
+                        selectAllBookmarksInFolder(bookmarkId)
+
+                    // Select the bookmark.
+                    bookmarksListView.setItemChecked(bookmarkPosition, true)
+                }
+
+                // Increment the bookmarks cursor position.
+                bookmarksCursor.moveToNext()
+            }
+
+            // Move to the next position.
+            folderCursor.moveToNext()
+        }
+    }
+
+    override fun onSaveBookmark(dialogFragment: DialogFragment, selectedBookmarkDatabaseId: Int, favoriteIconBitmap: Bitmap) {
+        // Get the dialog from the dialog fragment.
+        val dialog = dialogFragment.dialog!!
+
+        // Get handles for the views from dialog fragment.
+        val currentIconRadioButton = dialog.findViewById<RadioButton>(R.id.current_icon_radiobutton)
+        val bookmarkNameEditText = dialog.findViewById<EditText>(R.id.bookmark_name_edittext)
+        val bookmarkUrlEditText = dialog.findViewById<EditText>(R.id.bookmark_url_edittext)
+        val folderSpinner = dialog.findViewById<Spinner>(R.id.bookmark_folder_spinner)
+        val displayOrderEditText = dialog.findViewById<EditText>(R.id.bookmark_display_order_edittext)
+
+        // Extract the bookmark information.
+        val bookmarkNameString = bookmarkNameEditText.text.toString()
+        val bookmarkUrlString = bookmarkUrlEditText.text.toString()
+        val folderDatabaseId = folderSpinner.selectedItemId.toInt()
+        val displayOrderInt = displayOrderEditText.text.toString().toInt()
+
+        // Get the parent folder name.
+        val parentFolderNameString: String = if (folderDatabaseId == HOME_FOLDER_DATABASE_ID)  // The home folder is selected.  Use `""`.
+            ""
+        else  // Get the parent folder name from the database.
+            bookmarksDatabaseHelper.getFolderName(folderDatabaseId)
+
+        // Update the bookmark.
+        if (currentIconRadioButton.isChecked) {  // Update the bookmark without changing the favorite icon.
+            bookmarksDatabaseHelper.updateBookmark(selectedBookmarkDatabaseId, bookmarkNameString, bookmarkUrlString, parentFolderNameString, displayOrderInt)
+        } else {  // Update the bookmark using the `WebView` favorite icon.
+            // Create a favorite icon byte array output stream.
+            val newFavoriteIconByteArrayOutputStream = ByteArrayOutputStream()
+
+            // Convert the favorite icon bitmap to a byte array.  `0` is for lossless compression (the only option for a PNG).
+            favoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, newFavoriteIconByteArrayOutputStream)
+
+            // Convert the favorite icon byte array stream to a byte array.
+            val newFavoriteIconByteArray = newFavoriteIconByteArrayOutputStream.toByteArray()
+
+            //  Update the bookmark and the favorite icon.
+            bookmarksDatabaseHelper.updateBookmark(selectedBookmarkDatabaseId, bookmarkNameString, bookmarkUrlString, parentFolderNameString, displayOrderInt, newFavoriteIconByteArray)
+        }
+
+        // Update the list view.
+        updateBookmarksListView()
+    }
+
+    override fun onSaveBookmarkFolder(dialogFragment: DialogFragment, selectedFolderDatabaseId: Int, favoriteIconBitmap: Bitmap) {
+        // Get the dialog from the dialog fragment.
+        val dialog = dialogFragment.dialog!!
+
+        // Get handles for the views from dialog fragment.
+        val currentIconRadioButton = dialog.findViewById<RadioButton>(R.id.current_icon_radiobutton)
+        val defaultIconRadioButton = dialog.findViewById<RadioButton>(R.id.default_icon_radiobutton)
+        val defaultIconImageView = dialog.findViewById<ImageView>(R.id.default_icon_imageview)
+        val folderNameEditText = dialog.findViewById<EditText>(R.id.folder_name_edittext)
+        val parentFolderSpinner = dialog.findViewById<Spinner>(R.id.parent_folder_spinner)
+        val displayOrderEditText = dialog.findViewById<EditText>(R.id.display_order_edittext)
+
+        // Extract the folder information.
+        val newFolderNameString = folderNameEditText.text.toString()
+        val parentFolderDatabaseId = parentFolderSpinner.selectedItemId.toInt()
+        val displayOrderInt = displayOrderEditText.text.toString().toInt()
+
+        // Set the parent folder name.
+        val parentFolderNameString: String = if (parentFolderDatabaseId == HOME_FOLDER_DATABASE_ID)  // The home folder is selected.  Use `""`.
+            ""
+        else  // Get the parent folder name from the database.
+            bookmarksDatabaseHelper.getFolderName(parentFolderDatabaseId)
+
+        // Update the folder.
+        if (currentIconRadioButton.isChecked) {  // Update the folder without changing the favorite icon.
+            bookmarksDatabaseHelper.updateFolder(selectedFolderDatabaseId, oldFolderNameString, newFolderNameString, parentFolderNameString, displayOrderInt)
+        } else {  // Update the folder and the icon.
+            // Get the new folder icon bitmap.
+            val folderIconBitmap = if (defaultIconRadioButton.isChecked) {
+                // Get the default folder icon drawable.
+                val folderIconDrawable = defaultIconImageView.drawable
+
+                // Convert the folder icon drawable to a bitmap drawable.
+                val folderIconBitmapDrawable = folderIconDrawable as BitmapDrawable
+
+                // Convert the folder icon bitmap drawable to a bitmap.
+                folderIconBitmapDrawable.bitmap
+            } else {  // Use the `WebView` favorite icon.
+                // Get a copy of the favorite icon bitmap.
+                favoriteIconBitmap
+            }
+
+            // Create a folder icon byte array output stream.
+            val newFolderIconByteArrayOutputStream = ByteArrayOutputStream()
+
+            // Convert the folder icon bitmap to a byte array.  `0` is for lossless compression (the only option for a PNG).
+            folderIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, newFolderIconByteArrayOutputStream)
+
+            // Convert the folder icon byte array stream to a byte array.
+            val newFolderIconByteArray = newFolderIconByteArrayOutputStream.toByteArray()
+
+            //  Update the folder and the icon.
+            bookmarksDatabaseHelper.updateFolder(selectedFolderDatabaseId, oldFolderNameString, newFolderNameString, parentFolderNameString, displayOrderInt, newFolderIconByteArray)
+        }
+
+        // Update the list view.
+        updateBookmarksListView()
+    }
+
+    public override fun onDestroy() {
+        // Close the bookmarks cursor and database.
+        bookmarksCursor.close()
+        bookmarksDatabaseHelper.close()
+
+        // Run the default commands.
+        super.onDestroy()
+    }
+}
index ca9bfa3dc34ae24817d93973e15ae03fd60392eb..1895c6eb3e044fd698d8c617c82c743d31215c2d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017-2022 Soren Stoutner <soren@stoutner.com>.
+ * Copyright 2017-2023 Soren Stoutner <soren@stoutner.com>.
  *
  * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
  *
@@ -57,6 +57,19 @@ import com.stoutner.privacybrowser.fragments.DomainsListFragment.DismissSnackbar
 import com.stoutner.privacybrowser.fragments.DomainsListFragment.SaveDomainSettingsInterface
 import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper
 
+// Define the public constants.
+const val CLOSE_ON_BACK = "close_on_back"
+const val CURRENT_IP_ADDRESSES = "current_ip_addresses"
+const val LOAD_DOMAIN = "load_domain"
+const val SSL_END_DATE = "ssl_end_date"
+const val SSL_ISSUED_BY_CNAME = "ssl_issued_by_cname"
+const val SSL_ISSUED_BY_ONAME = "ssl_issued_by_oname"
+const val SSL_ISSUED_BY_UNAME = "ssl_issued_by_uname"
+const val SSL_ISSUED_TO_CNAME = "ssl_issued_to_cname"
+const val SSL_ISSUED_TO_ONAME = "ssl_issued_to_oname"
+const val SSL_ISSUED_TO_UNAME = "ssl_issued_to_uname"
+const val SSL_START_DATE = "ssl_start_date"
+
 // Define the class constants.
 private const val DOMAIN_SETTINGS_DATABASE_ID = "domain_settings_database_id"
 private const val DOMAIN_SETTINGS_DISPLAYED = "domain_settings_displayed"
@@ -65,20 +78,6 @@ private const val LISTVIEW_POSITION = "listview_position"
 
 class DomainsActivity : AppCompatActivity(), AddDomainListener, DismissSnackbarInterface, SaveDomainSettingsInterface {
     companion object {
-        // Define the public constants.
-        const val CLOSE_ON_BACK = "close_on_back"
-        const val CURRENT_IP_ADDRESSES = "current_ip_addresses"
-        const val CURRENT_URL = "current_url"
-        const val LOAD_DOMAIN = "load_domain"
-        const val SSL_END_DATE = "ssl_end_date"
-        const val SSL_ISSUED_BY_CNAME = "ssl_issued_by_cname"
-        const val SSL_ISSUED_BY_ONAME = "ssl_issued_by_oname"
-        const val SSL_ISSUED_BY_UNAME = "ssl_issued_by_uname"
-        const val SSL_ISSUED_TO_CNAME = "ssl_issued_to_cname"
-        const val SSL_ISSUED_TO_ONAME = "ssl_issued_to_oname"
-        const val SSL_ISSUED_TO_UNAME = "ssl_issued_to_uname"
-        const val SSL_START_DATE = "ssl_start_date"
-
         // Define the public variables.
         var currentDomainDatabaseId = 0  // Used in `DomainsListFragment`.
         var dismissingSnackbar = false  // Used in `DomainsListFragment`.
@@ -741,8 +740,12 @@ class DomainsActivity : AppCompatActivity(), AddDomainListener, DismissSnackbarI
 
         // Get the user agent name.
         val userAgentName: String = when (userAgentSwitchPosition) {
-            MainWebViewActivity.DOMAINS_SYSTEM_DEFAULT_USER_AGENT -> resources.getString(R.string.system_default_user_agent)  // Set the user agent name to be `System default user agent`.
-            MainWebViewActivity.DOMAINS_CUSTOM_USER_AGENT -> customUserAgentEditText.text.toString()  // Set the user agent name to be the custom user agent.
+            // Set the user agent name to be `System default user agent`.
+            DOMAINS_SYSTEM_DEFAULT_USER_AGENT -> resources.getString(R.string.system_default_user_agent)
+
+            // Set the user agent name to be the custom user agent.
+            DOMAINS_CUSTOM_USER_AGENT -> customUserAgentEditText.text.toString()
+
             else -> {
                 // Get the array of user agent names.
                 val userAgentNameArray = resources.getStringArray(R.array.user_agent_names)
index 25c2bfc6e3d3a1af9b47b0ba8bd5f482b6660be3..52a4075f7f3d61e2b16c1c26f3bcc22bff3fce50 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019-2022 Soren Stoutner <soren@stoutner.com>.
+ * Copyright 2019-2023 Soren Stoutner <soren@stoutner.com>.
  *
  * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
  *
@@ -21,7 +21,6 @@ package com.stoutner.privacybrowser.activities
 
 import android.content.ClipData
 import android.content.ClipboardManager
-import android.net.Uri
 import android.os.Build
 import android.os.Bundle
 import android.provider.OpenableColumns
@@ -67,7 +66,7 @@ class LogcatActivity : AppCompatActivity() {
     private lateinit var logcatTextView: TextView
 
     // Define the save logcat activity result launcher.  It must be defined before `onCreate()` is run or the app will crash.
-    private val saveLogcatActivityResultLauncher = registerForActivityResult(ActivityResultContracts.CreateDocument("text/plain")) { fileUri: Uri? ->
+    private val saveLogcatActivityResultLauncher = registerForActivityResult(ActivityResultContracts.CreateDocument("text/plain")) { fileUri ->
         // Only save the file if the URI is not null, which happens if the user exited the file picker by pressing back.
         if (fileUri != null) {
             try {
diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java
deleted file mode 100644 (file)
index 229ab3b..0000000
+++ /dev/null
@@ -1,6132 +0,0 @@
-/*
- * Copyright 2015-2023 Soren Stoutner <soren@stoutner.com>.
- *
- * Download cookie code contributed 2017 Hendrik Knackstedt.  Copyright assigned to Soren Stoutner <soren@stoutner.com>.
- *
- * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
- *
- * Privacy Browser Android 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 Android 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 Android.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.stoutner.privacybrowser.activities;
-
-import android.animation.ObjectAnimator;
-import android.annotation.SuppressLint;
-import android.app.Activity;
-import android.app.Dialog;
-import android.app.DownloadManager;
-import android.app.SearchManager;
-import android.content.ActivityNotFoundException;
-import android.content.BroadcastReceiver;
-import android.content.ClipData;
-import android.content.ClipboardManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
-import android.content.res.Configuration;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Typeface;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.net.http.SslCertificate;
-import android.net.http.SslError;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Environment;
-import android.os.Handler;
-import android.os.Message;
-import android.print.PrintDocumentAdapter;
-import android.print.PrintManager;
-import android.provider.DocumentsContract;
-import android.provider.OpenableColumns;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.text.style.ForegroundColorSpan;
-import android.util.Patterns;
-import android.util.TypedValue;
-import android.view.ContextMenu;
-import android.view.GestureDetector;
-import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.view.inputmethod.InputMethodManager;
-import android.webkit.CookieManager;
-import android.webkit.HttpAuthHandler;
-import android.webkit.SslErrorHandler;
-import android.webkit.ValueCallback;
-import android.webkit.WebBackForwardList;
-import android.webkit.WebChromeClient;
-import android.webkit.WebResourceRequest;
-import android.webkit.WebResourceResponse;
-import android.webkit.WebSettings;
-import android.webkit.WebStorage;
-import android.webkit.WebView;
-import android.webkit.WebViewClient;
-import android.webkit.WebViewDatabase;
-import android.widget.ArrayAdapter;
-import android.widget.CheckBox;
-import android.widget.CursorAdapter;
-import android.widget.EditText;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.ListView;
-import android.widget.ProgressBar;
-import android.widget.RadioButton;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
-
-import androidx.activity.OnBackPressedCallback;
-import androidx.activity.result.ActivityResult;
-import androidx.activity.result.ActivityResultCallback;
-import androidx.activity.result.ActivityResultLauncher;
-import androidx.activity.result.contract.ActivityResultContracts;
-import androidx.annotation.NonNull;
-import androidx.appcompat.app.ActionBar;
-import androidx.appcompat.app.ActionBarDrawerToggle;
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.appcompat.app.AppCompatDelegate;
-import androidx.appcompat.widget.Toolbar;
-import androidx.coordinatorlayout.widget.CoordinatorLayout;
-import androidx.core.content.res.ResourcesCompat;
-import androidx.core.view.GravityCompat;
-import androidx.drawerlayout.widget.DrawerLayout;
-import androidx.fragment.app.DialogFragment;
-import androidx.fragment.app.Fragment;
-import androidx.preference.PreferenceManager;
-import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
-import androidx.viewpager.widget.ViewPager;
-import androidx.webkit.WebSettingsCompat;
-import androidx.webkit.WebViewFeature;
-
-import com.google.android.material.appbar.AppBarLayout;
-import com.google.android.material.floatingactionbutton.FloatingActionButton;
-import com.google.android.material.navigation.NavigationView;
-import com.google.android.material.snackbar.Snackbar;
-import com.google.android.material.tabs.TabLayout;
-
-import com.stoutner.privacybrowser.R;
-import com.stoutner.privacybrowser.adapters.WebViewPagerAdapter;
-import com.stoutner.privacybrowser.coroutines.GetHostIpAddressesCoroutine;
-import com.stoutner.privacybrowser.coroutines.PopulateBlocklistsCoroutine;
-import com.stoutner.privacybrowser.coroutines.PrepareSaveDialogCoroutine;
-import com.stoutner.privacybrowser.coroutines.SaveUrlCoroutine;
-import com.stoutner.privacybrowser.coroutines.SaveWebpageImageCoroutine;
-import com.stoutner.privacybrowser.dataclasses.PendingDialogDataClass;
-import com.stoutner.privacybrowser.dialogs.CreateBookmarkDialog;
-import com.stoutner.privacybrowser.dialogs.CreateBookmarkFolderDialog;
-import com.stoutner.privacybrowser.dialogs.CreateHomeScreenShortcutDialog;
-import com.stoutner.privacybrowser.dialogs.FontSizeDialog;
-import com.stoutner.privacybrowser.dialogs.HttpAuthenticationDialog;
-import com.stoutner.privacybrowser.dialogs.OpenDialog;
-import com.stoutner.privacybrowser.dialogs.ProxyNotInstalledDialog;
-import com.stoutner.privacybrowser.dialogs.PinnedMismatchDialog;
-import com.stoutner.privacybrowser.dialogs.SaveDialog;
-import com.stoutner.privacybrowser.dialogs.SslCertificateErrorDialog;
-import com.stoutner.privacybrowser.dialogs.UrlHistoryDialog;
-import com.stoutner.privacybrowser.dialogs.ViewSslCertificateDialog;
-import com.stoutner.privacybrowser.dialogs.WaitingForProxyDialog;
-import com.stoutner.privacybrowser.fragments.WebViewTabFragment;
-import com.stoutner.privacybrowser.helpers.BlocklistHelper;
-import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper;
-import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper;
-import com.stoutner.privacybrowser.helpers.ProxyHelper;
-import com.stoutner.privacybrowser.helpers.SanitizeUrlHelper;
-import com.stoutner.privacybrowser.helpers.UrlHelper;
-import com.stoutner.privacybrowser.views.NestedScrollWebView;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.UnsupportedEncodingException;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.URLDecoder;
-import java.net.URLEncoder;
-
-import java.text.NumberFormat;
-
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-import kotlin.Pair;
-
-public class MainWebViewActivity extends AppCompatActivity implements CreateBookmarkDialog.CreateBookmarkListener, CreateBookmarkFolderDialog.CreateBookmarkFolderListener,
-        FontSizeDialog.UpdateFontSizeListener, NavigationView.OnNavigationItemSelectedListener, OpenDialog.OpenListener, PinnedMismatchDialog.PinnedMismatchListener,
-        PopulateBlocklistsCoroutine.PopulateBlocklistsListener, SaveDialog.SaveListener, UrlHistoryDialog.NavigateHistoryListener, WebViewTabFragment.NewTabListener {
-
-    // Define the public static variables.
-    public static final ExecutorService executorService = Executors.newFixedThreadPool(4);
-    public static String orbotStatus = "unknown";
-    public static final ArrayList<PendingDialogDataClass> pendingDialogsArrayList =  new ArrayList<>();
-    public static String proxyMode = ProxyHelper.NONE;
-
-    // Declare the public static variables.
-    public static String currentBookmarksFolder = "";
-    public static boolean restartFromBookmarksActivity;
-    public static WebViewPagerAdapter webViewPagerAdapter;
-
-    // Declare the public static views.
-    public static AppBarLayout appBarLayout;
-
-    // The user agent constants are public static so they can be accessed from `SettingsFragment`, `DomainsActivity`, and `DomainSettingsFragment`.
-    public final static int UNRECOGNIZED_USER_AGENT = -1;
-    public final static int SETTINGS_WEBVIEW_DEFAULT_USER_AGENT = 1;
-    public final static int SETTINGS_CUSTOM_USER_AGENT = 11;
-    public final static int DOMAINS_SYSTEM_DEFAULT_USER_AGENT = 0;
-    public final static int DOMAINS_WEBVIEW_DEFAULT_USER_AGENT = 2;
-    public final static int DOMAINS_CUSTOM_USER_AGENT = 12;
-
-    // Define the saved instance state constants.
-    private final String BOOKMARKS_DRAWER_PINNED = "bookmarks_drawer_pinned";
-    private final String PROXY_MODE = "proxy_mode";
-    private final String SAVED_STATE_ARRAY_LIST = "saved_state_array_list";
-    private final String SAVED_NESTED_SCROLL_WEBVIEW_STATE_ARRAY_LIST = "saved_nested_scroll_webview_state_array_list";
-    private final String SAVED_TAB_POSITION = "saved_tab_position";
-
-    // Define the saved instance state variables.
-    private ArrayList<Bundle> savedStateArrayList;
-    private ArrayList<Bundle> savedNestedScrollWebViewStateArrayList;
-    private int savedTabPosition;
-    private String savedProxyMode;
-
-    // The current WebView is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onRestart()`, `onCreateContextMenu()`, `findPreviousOnPage()`,
-    // `findNextOnPage()`, `closeFindOnPage()`, `loadUrlFromTextBox()`, `onSslMismatchBack()`, `applyProxy()`, and `applyDomainSettings()`.
-    private NestedScrollWebView currentWebView;
-
-    // The search URL is set in `applyAppSettings()` and used in `onNewIntent()`, `loadUrlFromTextBox()`, `initializeApp()`, and `initializeWebView()`.
-    private String searchURL;
-
-    // The blocklists are populated in `finishedPopulatingBlocklists()` 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[]>> ultraList;
-    private ArrayList<List<String[]>> ultraPrivacy;
-
-    // The action bar drawer toggle is initialized in `onCreate()` and used in `onResume()`.
-    private ActionBarDrawerToggle actionBarDrawerToggle;
-
-    // `bookmarksCursor` is used in `onDestroy()`, `onOptionsItemSelected()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and `loadBookmarksFolder()`.
-    private Cursor bookmarksCursor;
-
-    // `bookmarksCursorAdapter` is used in `onCreateBookmark()`, `onCreateBookmarkFolder()` `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and `loadBookmarksFolder()`.
-    private CursorAdapter bookmarksCursorAdapter;
-
-    // `fileChooserCallback` is used in `onCreate()` and `onActivityResult()`.
-    private ValueCallback<Uri[]> fileChooserCallback;
-
-    // The default progress view offsets are set in `onCreate()` and used in `initializeWebView()`.
-    private int appBarHeight;
-    private int defaultProgressViewStartOffset;
-    private int defaultProgressViewEndOffset;
-
-    // Declare the helpers.
-    private BookmarksDatabaseHelper bookmarksDatabaseHelper;
-    private DomainsDatabaseHelper domainsDatabaseHelper;
-    private ProxyHelper proxyHelper;
-
-    // Declare the class variables
-    private boolean bookmarksDrawerPinned;
-    private boolean bottomAppBar;
-    private boolean displayAdditionalAppBarIcons;
-    private boolean displayingFullScreenVideo;
-    private boolean downloadWithExternalApp;
-    private ForegroundColorSpan finalGrayColorSpan;
-    private boolean fullScreenBrowsingModeEnabled;
-    private boolean hideAppBar;
-    private boolean inFullScreenBrowsingMode;
-    private boolean incognitoModeEnabled;
-    private ForegroundColorSpan initialGrayColorSpan;
-    private boolean loadingNewIntent;
-    private BroadcastReceiver orbotStatusBroadcastReceiver;
-    private boolean reapplyAppSettingsOnRestart;
-    private boolean reapplyDomainSettingsOnRestart;
-    private ForegroundColorSpan redColorSpan;
-    private boolean sanitizeAmpRedirects;
-    private boolean sanitizeTrackingQueries;
-    private boolean scrollAppBar;
-    private boolean waitingForProxy;
-    private String webViewDefaultUserAgent;
-
-    // Define the class variables.
-    private ObjectAnimator objectAnimator = new ObjectAnimator();
-    private String saveUrlString = "";
-
-    // Declare the class views.
-    private ActionBar actionBar;
-    private CoordinatorLayout coordinatorLayout;
-    private ImageView bookmarksDrawerPinnedImageView;
-    private DrawerLayout drawerLayout;
-    private LinearLayout findOnPageLinearLayout;
-    private FrameLayout fullScreenVideoFrameLayout;
-    private FrameLayout rootFrameLayout;
-    private SwipeRefreshLayout swipeRefreshLayout;
-    private LinearLayout tabsLinearLayout;
-    private TabLayout tabLayout;
-    private Toolbar toolbar;
-    private EditText urlEditText;
-    private RelativeLayout urlRelativeLayout;
-    private ViewPager webViewPager;
-
-    // Declare the class menus.
-    private Menu optionsMenu;
-
-    // Declare the class menu items.
-    private MenuItem navigationBackMenuItem;
-    private MenuItem navigationForwardMenuItem;
-    private MenuItem navigationHistoryMenuItem;
-    private MenuItem navigationRequestsMenuItem;
-    private MenuItem optionsPrivacyMenuItem;
-    private MenuItem optionsRefreshMenuItem;
-    private MenuItem optionsCookiesMenuItem;
-    private MenuItem optionsDomStorageMenuItem;
-    private MenuItem optionsSaveFormDataMenuItem;
-    private MenuItem optionsClearDataMenuItem;
-    private MenuItem optionsClearCookiesMenuItem;
-    private MenuItem optionsClearDomStorageMenuItem;
-    private MenuItem optionsClearFormDataMenuItem;
-    private MenuItem optionsBlocklistsMenuItem;
-    private MenuItem optionsEasyListMenuItem;
-    private MenuItem optionsEasyPrivacyMenuItem;
-    private MenuItem optionsFanboysAnnoyanceListMenuItem;
-    private MenuItem optionsFanboysSocialBlockingListMenuItem;
-    private MenuItem optionsUltraListMenuItem;
-    private MenuItem optionsUltraPrivacyMenuItem;
-    private MenuItem optionsBlockAllThirdPartyRequestsMenuItem;
-    private MenuItem optionsProxyMenuItem;
-    private MenuItem optionsProxyNoneMenuItem;
-    private MenuItem optionsProxyTorMenuItem;
-    private MenuItem optionsProxyI2pMenuItem;
-    private MenuItem optionsProxyCustomMenuItem;
-    private MenuItem optionsUserAgentMenuItem;
-    private MenuItem optionsUserAgentPrivacyBrowserMenuItem;
-    private MenuItem optionsUserAgentWebViewDefaultMenuItem;
-    private MenuItem optionsUserAgentFirefoxOnAndroidMenuItem;
-    private MenuItem optionsUserAgentChromeOnAndroidMenuItem;
-    private MenuItem optionsUserAgentSafariOnIosMenuItem;
-    private MenuItem optionsUserAgentFirefoxOnLinuxMenuItem;
-    private MenuItem optionsUserAgentChromiumOnLinuxMenuItem;
-    private MenuItem optionsUserAgentFirefoxOnWindowsMenuItem;
-    private MenuItem optionsUserAgentChromeOnWindowsMenuItem;
-    private MenuItem optionsUserAgentEdgeOnWindowsMenuItem;
-    private MenuItem optionsUserAgentInternetExplorerOnWindowsMenuItem;
-    private MenuItem optionsUserAgentSafariOnMacosMenuItem;
-    private MenuItem optionsUserAgentCustomMenuItem;
-    private MenuItem optionsSwipeToRefreshMenuItem;
-    private MenuItem optionsWideViewportMenuItem;
-    private MenuItem optionsDisplayImagesMenuItem;
-    private MenuItem optionsDarkWebViewMenuItem;
-    private MenuItem optionsFontSizeMenuItem;
-    private MenuItem optionsAddOrEditDomainMenuItem;
-
-    // This variable won't be needed once the class is migrated to Kotlin, as can be seen in LogcatActivity or AboutVersionFragment.
-    private Activity resultLauncherActivityHandle;
-
-    // Define the save URL activity result launcher.  It must be defined before `onCreate()` is run or the app will crash.
-    private final ActivityResultLauncher<String> saveUrlActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.CreateDocument("*/*"),
-            new ActivityResultCallback<Uri>() {
-                @Override
-                public void onActivityResult(Uri fileUri) {
-                    // Only save the URL if the file URI is not null, which happens if the user exited the file picker by pressing back.
-                    if (fileUri != null) {
-                        // Instantiate the save URL coroutine.
-                        SaveUrlCoroutine saveUrlCoroutine = new SaveUrlCoroutine();
-
-                        // Save the URL.
-                        saveUrlCoroutine.save(getApplicationContext(), resultLauncherActivityHandle, saveUrlString, fileUri, currentWebView.getSettings().getUserAgentString(),
-                                currentWebView.getAcceptCookies());
-                    }
-
-                    // Reset the save URL string.
-                    saveUrlString = "";
-                }
-            });
-
-    // Define the save webpage archive activity result launcher.  It must be defined before `onCreate()` is run or the app will crash.
-    private final ActivityResultLauncher<String> saveWebpageArchiveActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.CreateDocument("multipart/related"),
-            new ActivityResultCallback<Uri>() {
-                @Override
-                public void onActivityResult(Uri fileUri) {
-                    // Only save the webpage archive if the file URI is not null, which happens if the user exited the file picker by pressing back.
-                    if (fileUri != null) {
-                        // Initialize the file name string from the file URI last path segment.
-                        String temporaryFileNameString = fileUri.getLastPathSegment();
-
-                        // Query the exact file name if the API >= 26.
-                        if (Build.VERSION.SDK_INT >= 26) {
-                            // Get a cursor from the content resolver.
-                            Cursor contentResolverCursor = resultLauncherActivityHandle.getContentResolver().query(fileUri, null, null, null);
-
-                            // Move to the fist row.
-                            contentResolverCursor.moveToFirst();
-
-                            // Get the file name from the cursor.
-                            temporaryFileNameString = contentResolverCursor.getString(contentResolverCursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME));
-
-                            // Close the cursor.
-                            contentResolverCursor.close();
-                        }
-
-                        // Save the final file name string so it can be used inside the lambdas.  This will no longer be needed once this activity has transitioned to Kotlin.
-                        String finalFileNameString = temporaryFileNameString;
-
-                        try {
-                            // Create a temporary MHT file.
-                            File temporaryMhtFile = File.createTempFile("temporary_mht_file", ".mht", getCacheDir());
-                            currentWebView.saveWebArchive(temporaryMhtFile.toString(), false, callbackValue -> {
-                                if (callbackValue != null) {  // The temporary MHT file was saved successfully.
-                                    try {
-                                        // Create a temporary MHT file input stream.
-                                        FileInputStream temporaryMhtFileInputStream = new FileInputStream(temporaryMhtFile);
-
-                                        // Get an output stream for the save webpage file path.
-                                        OutputStream mhtOutputStream = getContentResolver().openOutputStream(fileUri);
-
-                                        // Create a transfer byte array.
-                                        byte[] transferByteArray = new byte[1024];
-
-                                        // Create an integer to track the number of bytes read.
-                                        int bytesRead;
-
-                                        // Copy the temporary MHT file input stream to the MHT output stream.
-                                        while ((bytesRead = temporaryMhtFileInputStream.read(transferByteArray)) > 0) {
-                                            mhtOutputStream.write(transferByteArray, 0, bytesRead);
-                                        }
-
-                                        // Close the streams.
-                                        mhtOutputStream.close();
-                                        temporaryMhtFileInputStream.close();
-
-                                        // Display a snackbar.
-                                        Snackbar.make(currentWebView, getString(R.string.saved, finalFileNameString), Snackbar.LENGTH_SHORT).show();
-                                    } catch (Exception exception) {
-                                        // Display a snackbar with the exception.
-                                        Snackbar.make(currentWebView, getString(R.string.error_saving_file, finalFileNameString, exception), Snackbar.LENGTH_INDEFINITE).show();
-                                    } finally {
-                                        // Delete the temporary MHT file.
-                                        //noinspection ResultOfMethodCallIgnored
-                                        temporaryMhtFile.delete();
-                                    }
-                                } else {  // There was an unspecified error while saving the temporary MHT file.
-                                    // Display an error snackbar.
-                                    Snackbar.make(currentWebView, getString(R.string.error_saving_file, finalFileNameString, getString(R.string.unknown_error)), Snackbar.LENGTH_INDEFINITE).show();
-                                }
-                            });
-                        } catch (IOException ioException) {
-                            // Display a snackbar with the IO exception.
-                            Snackbar.make(currentWebView, getString(R.string.error_saving_file, finalFileNameString, ioException), Snackbar.LENGTH_INDEFINITE).show();
-                        }
-                    }
-                }
-            });
-
-    // Define the save webpage image activity result launcher.  It must be defined before `onCreate()` is run or the app will crash.
-    private final ActivityResultLauncher<String> saveWebpageImageActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.CreateDocument("image/png"),
-            new ActivityResultCallback<Uri>() {
-                @Override
-                public void onActivityResult(Uri fileUri) {
-                    // Only save the webpage image if the file URI is not null, which happens if the user exited the file picker by pressing back.
-                    if (fileUri != null) {
-                        // Instantiate the save webpage image coroutine.
-                        SaveWebpageImageCoroutine saveWebpageImageCoroutine = new SaveWebpageImageCoroutine();
-
-                        // Save the webpage image.
-                        saveWebpageImageCoroutine.save(resultLauncherActivityHandle, fileUri, currentWebView);
-                    }
-                }
-            });
-
-    // Define the save webpage image activity result launcher.  It must be defined before `onCreate()` is run or the app will crash.
-    private final ActivityResultLauncher<Intent> browseFileUploadActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
-            new ActivityResultCallback<ActivityResult>() {
-                @Override
-                public void onActivityResult(ActivityResult activityResult) {
-                    // Pass the file to the WebView.
-                    fileChooserCallback.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(activityResult.getResultCode(), activityResult.getData()));
-                }
-            });
-
-    // Remove the warning about needing to override `performClick()` when using an `OnTouchListener` with WebView.
-    @SuppressLint("ClickableViewAccessibility")
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        // Run the default commands.
-        super.onCreate(savedInstanceState);
-
-        // Populate the result launcher activity.  This will no longer be needed once the activity has transitioned to Kotlin.
-        resultLauncherActivityHandle = this;
-
-        // 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);
-
-        // Get a handle for the shared preferences.
-        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
-
-        // Get the preferences.
-        String appTheme = sharedPreferences.getString(getString(R.string.app_theme_key), getString(R.string.app_theme_default_value));
-        boolean allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false);
-        bottomAppBar = sharedPreferences.getBoolean(getString(R.string.bottom_app_bar_key), false);
-        displayAdditionalAppBarIcons = sharedPreferences.getBoolean(getString(R.string.display_additional_app_bar_icons_key), false);
-
-        // Get the theme entry values string array.
-        String[] appThemeEntryValuesStringArray = getResources().getStringArray(R.array.app_theme_entry_values);
-
-        // Get the current theme status.
-        int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
-
-        // Set the app theme according to the preference.  A switch statement cannot be used because the theme entry values string array is not a compile time constant.
-        if (appTheme.equals(appThemeEntryValuesStringArray[1])) {  // The light theme is selected.
-            // Apply the light theme.
-            AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
-        } else if (appTheme.equals(appThemeEntryValuesStringArray[2])) {  // The dark theme is selected.
-            // Apply the dark theme.
-            AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
-        } else {  // The system default theme is selected.
-            if (Build.VERSION.SDK_INT >= 28) {  // The system default theme is supported.
-                // Follow the system default theme.
-                AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
-            } else {  // The system default theme is not supported.
-                // Follow the battery saver mode.
-                AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY);
-            }
-        }
-
-        // Do not continue if the app theme is different than the OS theme.  The app always initially starts in the OS theme.
-        // If the user has specified the opposite theme should be used, the app will restart in that mode after the above `setDefaultNightMode()` code processes.  However, the restart is delayed.
-        // If the blacklist coroutine starts below it will continue to run during the restart, which leads to indeterminate behavior, with the system often not knowing how many tabs exist.
-        // See https://redmine.stoutner.com/issues/952.
-        if (appTheme.equals(appThemeEntryValuesStringArray[0]) ||  // The system default theme is used.
-                (appTheme.equals(appThemeEntryValuesStringArray[1]) && currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) ||  // The app is running in day theme as desired.
-                (appTheme.equals(appThemeEntryValuesStringArray[2]) && currentThemeStatus == Configuration.UI_MODE_NIGHT_YES)) {  // The app is running in night theme as desired.
-
-            // Disable screenshots if not allowed.
-            if (!allowScreenshots) {
-                getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
-            }
-
-            // Check to see if the activity has been restarted.
-            if (savedInstanceState != null) {
-                // Store the saved instance state variables.
-                bookmarksDrawerPinned = savedInstanceState.getBoolean(BOOKMARKS_DRAWER_PINNED);
-                savedStateArrayList = savedInstanceState.getParcelableArrayList(SAVED_STATE_ARRAY_LIST);
-                savedNestedScrollWebViewStateArrayList = savedInstanceState.getParcelableArrayList(SAVED_NESTED_SCROLL_WEBVIEW_STATE_ARRAY_LIST);
-                savedTabPosition = savedInstanceState.getInt(SAVED_TAB_POSITION);
-                savedProxyMode = savedInstanceState.getString(PROXY_MODE);
-            }
-
-            // Enable the drawing of the entire webpage.  This makes it possible to save a website image.  This must be done before anything else happens with the WebView.
-            WebView.enableSlowWholeDocumentDraw();
-
-            // Set the content view according to the position of the app bar.
-            if (bottomAppBar) setContentView(R.layout.main_framelayout_bottom_appbar);
-            else setContentView(R.layout.main_framelayout_top_appbar);
-
-            // Get handles for the views.
-            rootFrameLayout = findViewById(R.id.root_framelayout);
-            drawerLayout = findViewById(R.id.drawerlayout);
-            coordinatorLayout = findViewById(R.id.coordinatorlayout);
-            appBarLayout = findViewById(R.id.appbar_layout);
-            toolbar = findViewById(R.id.toolbar);
-            findOnPageLinearLayout = findViewById(R.id.find_on_page_linearlayout);
-            tabsLinearLayout = findViewById(R.id.tabs_linearlayout);
-            tabLayout = findViewById(R.id.tablayout);
-            swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
-            webViewPager = findViewById(R.id.webviewpager);
-            NavigationView navigationView = findViewById(R.id.navigationview);
-            bookmarksDrawerPinnedImageView = findViewById(R.id.bookmarks_drawer_pinned_imageview);
-            fullScreenVideoFrameLayout = findViewById(R.id.full_screen_video_framelayout);
-
-            // Get a handle for the navigation menu.
-            Menu navigationMenu = navigationView.getMenu();
-
-            // Get handles for the navigation menu items.
-            navigationBackMenuItem = navigationMenu.findItem(R.id.back);
-            navigationForwardMenuItem = navigationMenu.findItem(R.id.forward);
-            navigationHistoryMenuItem = navigationMenu.findItem(R.id.history);
-            navigationRequestsMenuItem = navigationMenu.findItem(R.id.requests);
-
-            // Listen for touches on the navigation menu.
-            navigationView.setNavigationItemSelectedListener(this);
-
-            // Get a handle for the app compat delegate.
-            AppCompatDelegate appCompatDelegate = getDelegate();
-
-            // Set the support action bar.
-            appCompatDelegate.setSupportActionBar(toolbar);
-
-            // Get a handle for the action bar.
-            actionBar = appCompatDelegate.getSupportActionBar();
-
-            // Remove the incorrect lint warning below that the action bar might be null.
-            assert actionBar != null;
-
-            // Add the custom layout, which shows the URL text bar.
-            actionBar.setCustomView(R.layout.url_app_bar);
-            actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
-
-            // Get handles for the views in the URL app bar.
-            urlRelativeLayout = findViewById(R.id.url_relativelayout);
-            urlEditText = findViewById(R.id.url_edittext);
-
-            // Create the hamburger icon at the start of the AppBar.
-            actionBarDrawerToggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.open_navigation_drawer, R.string.close_navigation_drawer);
-
-            // Initially disable the sliding drawers.  They will be enabled once the blocklists are loaded.
-            drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
-
-            // Initially hide the user interface so that only the blocklist loading screen is shown (if reloading).
-            drawerLayout.setVisibility(View.GONE);
-
-            // Initialize the web view pager adapter.
-            webViewPagerAdapter = new WebViewPagerAdapter(getSupportFragmentManager());
-
-            // Set the pager adapter on the web view pager.
-            webViewPager.setAdapter(webViewPagerAdapter);
-
-            // Store up to 100 tabs in memory.
-            webViewPager.setOffscreenPageLimit(100);
-
-            // Instantiate the helpers.
-            bookmarksDatabaseHelper = new BookmarksDatabaseHelper(this);
-            domainsDatabaseHelper = new DomainsDatabaseHelper(this);
-            proxyHelper = new ProxyHelper();
-
-            // Update the bookmarks drawer pinned image view.
-            updateBookmarksDrawerPinnedImageView();
-
-            // Initialize the app.
-            initializeApp();
-
-            // Apply the app settings from the shared preferences.
-            applyAppSettings();
-
-            // Control what the system back command does.
-            OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(true) {
-                @Override
-                public void handleOnBackPressed() {
-                    // Process the different back options.
-                    if (drawerLayout.isDrawerVisible(GravityCompat.START)) {  // The navigation drawer is open.
-                        // Close the navigation drawer.
-                        drawerLayout.closeDrawer(GravityCompat.START);
-                    } else if (drawerLayout.isDrawerVisible(GravityCompat.END)) {  // The bookmarks drawer is open.
-                        // close the bookmarks drawer.
-                        drawerLayout.closeDrawer(GravityCompat.END);
-                    } else if (displayingFullScreenVideo) {  // A full screen video is shown.
-                        // Exit the full screen video.
-                        exitFullScreenVideo();
-                        // It shouldn't be possible for the currentWebView to be null, but crash logs indicate it sometimes happens.
-                    } else if ((currentWebView != null) && (currentWebView.canGoBack())) {  // There is at least one item in the current WebView history.
-                        // Get the current web back forward list.
-                        WebBackForwardList webBackForwardList = currentWebView.copyBackForwardList();
-
-                        // Get the previous entry URL.
-                        String previousUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() - 1).getUrl();
-
-                        // Apply the domain settings.
-                        applyDomainSettings(currentWebView, previousUrl, false, false, false);
-
-                        // Go back.
-                        currentWebView.goBack();
-                    } else if (tabLayout.getTabCount() > 1) {  // There are at least two tabs.
-                        // Close the current tab.
-                        closeCurrentTab();
-                    } else {  // There isn't anything to do in Privacy Browser.
-                        // Run clear and exit.
-                        clearAndExit();
-                    }
-                }
-            };
-
-            // Register the on back pressed callback.
-            getOnBackPressedDispatcher().addCallback(this, onBackPressedCallback);
-
-            // Instantiate the populate blocklists coroutine.
-            PopulateBlocklistsCoroutine populateBlocklistsCoroutine = new PopulateBlocklistsCoroutine(this);
-
-            // Populate the blocklists.
-            populateBlocklistsCoroutine.populateBlocklists(this);
-        }
-    }
-
-    @Override
-    protected void onNewIntent(Intent intent) {
-        // Run the default commands.
-        super.onNewIntent(intent);
-
-        // Check to see if the app is being restarted from a saved state.
-        if (savedStateArrayList == null || savedStateArrayList.size() == 0) {  // The activity is not being restarted from a saved state.
-            // Get the information from the intent.
-            String intentAction = intent.getAction();
-            Uri intentUriData = intent.getData();
-            String intentStringExtra = intent.getStringExtra(Intent.EXTRA_TEXT);
-
-            // Determine if this is a web search.
-            boolean isWebSearch = ((intentAction != null) && intentAction.equals(Intent.ACTION_WEB_SEARCH));
-
-            // Only process the URI if it contains data or it is a web search.  If the user pressed the desktop icon after the app was already running the URI will be null.
-            if (intentUriData != null || intentStringExtra != null || isWebSearch) {
-                // Exit the full screen video if it is displayed.
-                if (displayingFullScreenVideo) {
-                    // Exit full screen video mode.
-                    exitFullScreenVideo();
-
-                    // Reload the current WebView.  Otherwise, it can display entirely black.
-                    currentWebView.reload();
-                }
-
-                // Get the shared preferences.
-                SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
-
-                // Create a URL string.
-                String url;
-
-                // If the intent action is a web search, perform the search.
-                if (isWebSearch) {  // The intent is a web search.
-                    // Create an encoded URL string.
-                    String encodedUrlString;
-
-                    // Sanitize the search input and convert it to a search.
-                    try {
-                        encodedUrlString = URLEncoder.encode(intent.getStringExtra(SearchManager.QUERY), "UTF-8");
-                    } catch (UnsupportedEncodingException exception) {
-                        encodedUrlString = "";
-                    }
-
-                    // Add the base search URL.
-                    url = searchURL + encodedUrlString;
-                } else if (intentUriData != null) {  // The intent contains a URL formatted as a URI.
-                    // Set the intent data as the URL.
-                    url = intentUriData.toString();
-                } else {  // The intent contains a string, which might be a URL.
-                    // Set the intent string as the URL.
-                    url = intentStringExtra;
-                }
-
-                // Add a new tab if specified in the preferences.
-                if (sharedPreferences.getBoolean(getString(R.string.open_intents_in_new_tab_key), true)) {  // Load the URL in a new tab.
-                    // Set the loading new intent flag.
-                    loadingNewIntent = true;
-
-                    // Add a new tab.
-                    addNewTab(url, true);
-                } else {  // Load the URL in the current tab.
-                    // Make it so.
-                    loadUrl(currentWebView, url);
-                }
-
-                // Close the navigation drawer if it is open.
-                if (drawerLayout.isDrawerVisible(GravityCompat.START)) {
-                    drawerLayout.closeDrawer(GravityCompat.START);
-                }
-
-                // Close the bookmarks drawer if it is open.
-                if (drawerLayout.isDrawerVisible(GravityCompat.END)) {
-                    drawerLayout.closeDrawer(GravityCompat.END);
-                }
-            }
-        } else {  // The app has been restarted.
-            // Get the information from the intent.
-            String intentAction = intent.getAction();
-            Uri intentUriData = intent.getData();
-            String intentStringExtra = intent.getStringExtra(Intent.EXTRA_TEXT);
-
-            // Determine if this is a web search.
-            boolean isWebSearch = ((intentAction != null) && intentAction.equals(Intent.ACTION_WEB_SEARCH));
-
-            // If the new intent will open a new tab, set the saved tab position to be the size of the saved state array list.
-            // The tab position is 0 based, meaning the at the new tab will be the tab position that is restored.
-            if (intentUriData != null || intentStringExtra != null || isWebSearch)
-                savedTabPosition = savedStateArrayList.size();
-
-            // Replace the intent that started the app with this one.  This will load the tab after the others have been restored.
-            setIntent(intent);
-        }
-    }
-
-    @Override
-    public void onRestart() {
-        // Run the default commands.
-        super.onRestart();
-
-        // Apply the app settings if returning from the Settings activity.
-        if (reapplyAppSettingsOnRestart) {
-            // Reset the reapply app settings on restart tracker.
-            reapplyAppSettingsOnRestart = false;
-
-            // Apply the app settings.
-            applyAppSettings();
-        }
-
-        // Apply the domain settings if returning from the settings or domains activity.
-        if (reapplyDomainSettingsOnRestart) {
-            // Reset the reapply domain settings on restart tracker.
-            reapplyDomainSettingsOnRestart = false;
-
-            // Reapply the domain settings for each tab.
-            for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
-                // Get the WebView tab fragment.
-                WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i);
-
-                // Get the fragment view.
-                View fragmentView = webViewTabFragment.getView();
-
-                // Only reload the WebViews if they exist.
-                if (fragmentView != null) {
-                    // Get the nested scroll WebView from the tab fragment.
-                    NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
-
-                    // Reset the current domain name so the domain settings will be reapplied.
-                    nestedScrollWebView.setCurrentDomainName("");
-
-                    // Reapply the domain settings if the URL is not null, which can happen if an empty tab is active when returning from settings.
-                    if (nestedScrollWebView.getUrl() != null) {
-                        applyDomainSettings(nestedScrollWebView, nestedScrollWebView.getUrl(), false, true, false);
-                    }
-                }
-            }
-        }
-
-        // Update the bookmarks drawer if returning from the Bookmarks activity.
-        if (restartFromBookmarksActivity) {
-            // Close the bookmarks drawer.
-            drawerLayout.closeDrawer(GravityCompat.END);
-
-            // Reload the bookmarks drawer.
-            loadBookmarksFolder();
-
-            // Reset `restartFromBookmarksActivity`.
-            restartFromBookmarksActivity = false;
-        }
-
-        // Update the privacy icon.  `true` runs `invalidateOptionsMenu` as the last step.  This can be important if the screen was rotated.
-        updatePrivacyIcons(true);
-    }
-
-    // `onStart()` runs after `onCreate()` or `onRestart()`.  This is used instead of `onResume()` so the commands aren't called every time the screen is partially hidden.
-    @Override
-    public void onStart() {
-        // Run the default commands.
-        super.onStart();
-
-        // Resume any WebViews if the pager adapter exists.  If the app is restarting to change the initial app theme it won't have been populated yet.
-        if (webViewPagerAdapter != null) {
-            for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
-                // Get the WebView tab fragment.
-                WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i);
-
-                // Get the fragment view.
-                View fragmentView = webViewTabFragment.getView();
-
-                // Only resume the WebViews if they exist (they won't when the app is first created).
-                if (fragmentView != null) {
-                    // Get the nested scroll WebView from the tab fragment.
-                    NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
-
-                    // Resume the nested scroll WebView.
-                    nestedScrollWebView.onResume();
-                }
-            }
-        }
-
-        // Resume the nested scroll WebView JavaScript timers.  This is a global command that resumes JavaScript timers on all WebViews.
-        if (currentWebView != null) {
-            currentWebView.resumeTimers();
-        }
-
-        // Reapply the proxy settings if the system is using a proxy.  This redisplays the appropriate alert dialog.
-        if (!proxyMode.equals(ProxyHelper.NONE)) {
-            applyProxy(false);
-        }
-
-        // Reapply any system UI flags.
-        if (displayingFullScreenVideo || inFullScreenBrowsingMode) {  // The system is displaying a website or a video in full screen mode.
-            /* Hide the system bars.
-             * SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen.
-             * SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN makes the root frame layout fill the area that is normally reserved for the status bar.
-             * SYSTEM_UI_FLAG_HIDE_NAVIGATION hides the navigation bar on the bottom or right of the screen.
-             * SYSTEM_UI_FLAG_IMMERSIVE_STICKY makes the status and navigation bars translucent and automatically re-hides them after they are shown.
-             */
-            rootFrameLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
-                    View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
-        }
-
-        // Show any pending dialogs.
-        for (int i = 0; i < pendingDialogsArrayList.size(); i++) {
-            // Get the pending dialog from the array list.
-            PendingDialogDataClass pendingDialogDataClass = pendingDialogsArrayList.get(i);
-
-            // Show the pending dialog.
-            pendingDialogDataClass.dialogFragment.show(getSupportFragmentManager(), pendingDialogDataClass.tag);
-        }
-
-        // Clear the pending dialogs array list.
-        pendingDialogsArrayList.clear();
-    }
-
-    // `onStop()` runs after `onPause()`.  It is used instead of `onPause()` so the commands are not called every time the screen is partially hidden.
-    @Override
-    public void onStop() {
-        // Run the default commands.
-        super.onStop();
-
-        // Only pause the WebViews if the pager adapter is not null, which is the case if the app is restarting to change the initial app theme.
-        if (webViewPagerAdapter != null) {
-            // Pause each web view.
-            for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
-                // Get the WebView tab fragment.
-                WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i);
-
-                // Get the fragment view.
-                View fragmentView = webViewTabFragment.getView();
-
-                // Only pause the WebViews if they exist (they won't when the app is first created).
-                if (fragmentView != null) {
-                    // Get the nested scroll WebView from the tab fragment.
-                    NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
-
-                    // Pause the nested scroll WebView.
-                    nestedScrollWebView.onPause();
-                }
-            }
-        }
-
-        // Pause the WebView JavaScript timers.  This is a global command that pauses JavaScript on all WebViews.
-        if (currentWebView != null) {
-            currentWebView.pauseTimers();
-        }
-    }
-
-    @Override
-    public void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
-        // Run the default commands.
-        super.onSaveInstanceState(savedInstanceState);
-
-        // Only save the instance state if the WebView pager adapter is not null, which will be the case if the app is restarting to change the initial app theme.
-        if (webViewPagerAdapter != null) {
-            // Create the saved state array lists.
-            ArrayList<Bundle> savedStateArrayList = new ArrayList<>();
-            ArrayList<Bundle> savedNestedScrollWebViewStateArrayList = new ArrayList<>();
-
-            // Get the URLs from each tab.
-            for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
-                // Get the WebView tab fragment.
-                WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i);
-
-                // Get the fragment view.
-                View fragmentView = webViewTabFragment.getView();
-
-                if (fragmentView != null) {
-                    // Get the nested scroll WebView from the tab fragment.
-                    NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
-
-                    // Create saved state bundle.
-                    Bundle savedStateBundle = new Bundle();
-
-                    // Get the current states.
-                    nestedScrollWebView.saveState(savedStateBundle);
-                    Bundle savedNestedScrollWebViewStateBundle = nestedScrollWebView.saveNestedScrollWebViewState();
-
-                    // Store the saved states in the array lists.
-                    savedStateArrayList.add(savedStateBundle);
-                    savedNestedScrollWebViewStateArrayList.add(savedNestedScrollWebViewStateBundle);
-                }
-            }
-
-            // Get the current tab position.
-            int currentTabPosition = tabLayout.getSelectedTabPosition();
-
-            // Store the saved states in the bundle.
-            savedInstanceState.putBoolean(BOOKMARKS_DRAWER_PINNED, bookmarksDrawerPinned);
-            savedInstanceState.putString(PROXY_MODE, proxyMode);
-            savedInstanceState.putParcelableArrayList(SAVED_STATE_ARRAY_LIST, savedStateArrayList);
-            savedInstanceState.putParcelableArrayList(SAVED_NESTED_SCROLL_WEBVIEW_STATE_ARRAY_LIST, savedNestedScrollWebViewStateArrayList);
-            savedInstanceState.putInt(SAVED_TAB_POSITION, currentTabPosition);
-        }
-    }
-
-    @Override
-    public void onDestroy() {
-        // Unregister the orbot status broadcast receiver if it exists.
-        if (orbotStatusBroadcastReceiver != null) {
-            this.unregisterReceiver(orbotStatusBroadcastReceiver);
-        }
-
-        // Close the bookmarks cursor if it exists.
-        if (bookmarksCursor != null) {
-            bookmarksCursor.close();
-        }
-
-        // Close the bookmarks database if it exists.
-        if (bookmarksDatabaseHelper != null) {
-            bookmarksDatabaseHelper.close();
-        }
-
-        // Run the default commands.
-        super.onDestroy();
-    }
-
-    @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
-        // Inflate the menu.  This adds items to the action bar if it is present.
-        getMenuInflater().inflate(R.menu.webview_options_menu, menu);
-
-        // Store a handle for the options menu so it can be used by `onOptionsItemSelected()` and `updatePrivacyIcons()`.
-        optionsMenu = menu;
-
-        // Get handles for the menu items.
-        optionsPrivacyMenuItem = menu.findItem(R.id.javascript);
-        optionsRefreshMenuItem = menu.findItem(R.id.refresh);
-        MenuItem bookmarksMenuItem = menu.findItem(R.id.bookmarks);
-        optionsCookiesMenuItem = menu.findItem(R.id.cookies);
-        optionsDomStorageMenuItem = menu.findItem(R.id.dom_storage);
-        optionsSaveFormDataMenuItem = menu.findItem(R.id.save_form_data);  // Form data can be removed once the minimum API >= 26.
-        optionsClearDataMenuItem = menu.findItem(R.id.clear_data);
-        optionsClearCookiesMenuItem = menu.findItem(R.id.clear_cookies);
-        optionsClearDomStorageMenuItem = menu.findItem(R.id.clear_dom_storage);
-        optionsClearFormDataMenuItem = menu.findItem(R.id.clear_form_data);  // Form data can be removed once the minimum API >= 26.
-        optionsBlocklistsMenuItem = menu.findItem(R.id.blocklists);
-        optionsEasyListMenuItem = menu.findItem(R.id.easylist);
-        optionsEasyPrivacyMenuItem = menu.findItem(R.id.easyprivacy);
-        optionsFanboysAnnoyanceListMenuItem = menu.findItem(R.id.fanboys_annoyance_list);
-        optionsFanboysSocialBlockingListMenuItem = menu.findItem(R.id.fanboys_social_blocking_list);
-        optionsUltraListMenuItem = menu.findItem(R.id.ultralist);
-        optionsUltraPrivacyMenuItem = menu.findItem(R.id.ultraprivacy);
-        optionsBlockAllThirdPartyRequestsMenuItem = menu.findItem(R.id.block_all_third_party_requests);
-        optionsProxyMenuItem = menu.findItem(R.id.proxy);
-        optionsProxyNoneMenuItem = menu.findItem(R.id.proxy_none);
-        optionsProxyTorMenuItem = menu.findItem(R.id.proxy_tor);
-        optionsProxyI2pMenuItem = menu.findItem(R.id.proxy_i2p);
-        optionsProxyCustomMenuItem = menu.findItem(R.id.proxy_custom);
-        optionsUserAgentMenuItem = menu.findItem(R.id.user_agent);
-        optionsUserAgentPrivacyBrowserMenuItem = menu.findItem(R.id.user_agent_privacy_browser);
-        optionsUserAgentWebViewDefaultMenuItem = menu.findItem(R.id.user_agent_webview_default);
-        optionsUserAgentFirefoxOnAndroidMenuItem = menu.findItem(R.id.user_agent_firefox_on_android);
-        optionsUserAgentChromeOnAndroidMenuItem = menu.findItem(R.id.user_agent_chrome_on_android);
-        optionsUserAgentSafariOnIosMenuItem = menu.findItem(R.id.user_agent_safari_on_ios);
-        optionsUserAgentFirefoxOnLinuxMenuItem = menu.findItem(R.id.user_agent_firefox_on_linux);
-        optionsUserAgentChromiumOnLinuxMenuItem = menu.findItem(R.id.user_agent_chromium_on_linux);
-        optionsUserAgentFirefoxOnWindowsMenuItem = menu.findItem(R.id.user_agent_firefox_on_windows);
-        optionsUserAgentChromeOnWindowsMenuItem = menu.findItem(R.id.user_agent_chrome_on_windows);
-        optionsUserAgentEdgeOnWindowsMenuItem = menu.findItem(R.id.user_agent_edge_on_windows);
-        optionsUserAgentInternetExplorerOnWindowsMenuItem = menu.findItem(R.id.user_agent_internet_explorer_on_windows);
-        optionsUserAgentSafariOnMacosMenuItem = menu.findItem(R.id.user_agent_safari_on_macos);
-        optionsUserAgentCustomMenuItem = menu.findItem(R.id.user_agent_custom);
-        optionsSwipeToRefreshMenuItem = menu.findItem(R.id.swipe_to_refresh);
-        optionsWideViewportMenuItem = menu.findItem(R.id.wide_viewport);
-        optionsDisplayImagesMenuItem = menu.findItem(R.id.display_images);
-        optionsDarkWebViewMenuItem = menu.findItem(R.id.dark_webview);
-        optionsFontSizeMenuItem = menu.findItem(R.id.font_size);
-        optionsAddOrEditDomainMenuItem = menu.findItem(R.id.add_or_edit_domain);
-
-        // Set the initial status of the privacy icons.  `false` does not call `invalidateOptionsMenu` as the last step.
-        updatePrivacyIcons(false);
-
-        // Only display the form data menu items if the API < 26.
-        optionsSaveFormDataMenuItem.setVisible(Build.VERSION.SDK_INT < 26);
-        optionsClearFormDataMenuItem.setVisible(Build.VERSION.SDK_INT < 26);
-
-        // Disable the clear form data menu item if the API >= 26 so that the status of the main Clear Data is calculated correctly.
-        optionsClearFormDataMenuItem.setEnabled(Build.VERSION.SDK_INT < 26);
-
-        // Only display the dark WebView menu item if the API >= 29.
-        optionsDarkWebViewMenuItem.setVisible(Build.VERSION.SDK_INT >= 29);
-
-        // Set the status of the additional app bar icons.  Setting the refresh menu item to `SHOW_AS_ACTION_ALWAYS` makes it appear even on small devices like phones.
-        if (displayAdditionalAppBarIcons) {
-            optionsRefreshMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
-            bookmarksMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
-            optionsCookiesMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
-        } else { //Do not display the additional icons.
-            optionsRefreshMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
-            bookmarksMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
-            optionsCookiesMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
-        }
-
-        // Replace Refresh with Stop if a URL is already loading.
-        if (currentWebView != null && currentWebView.getProgress() != 100) {
-            // Set the title.
-            optionsRefreshMenuItem.setTitle(R.string.stop);
-
-            // Set the icon if it is displayed in the app bar.  Once the minimum API is >= 26, the blue and black icons can be combined with a tint list.
-            if (displayAdditionalAppBarIcons) {
-                optionsRefreshMenuItem.setIcon(R.drawable.close_blue);
-            }
-        }
-
-        // Done.
-        return true;
-    }
-
-    @Override
-    public boolean onPrepareOptionsMenu(Menu menu) {
-        // Get a handle for the cookie manager.
-        CookieManager cookieManager = CookieManager.getInstance();
-
-        // Initialize the current user agent string and the font size.
-        String currentUserAgent = getString(R.string.user_agent_privacy_browser);
-        int fontSize = 100;
-
-        // Set items that require the current web view to be populated.  It will be null when the program is first opened, as `onPrepareOptionsMenu()` is called before the first WebView is initialized.
-        if (currentWebView != null) {
-            // Set the add or edit domain text.
-            if (currentWebView.getDomainSettingsApplied()) {
-                optionsAddOrEditDomainMenuItem.setTitle(R.string.edit_domain_settings);
-            } else {
-                optionsAddOrEditDomainMenuItem.setTitle(R.string.add_domain_settings);
-            }
-
-            // Get the current user agent from the WebView.
-            currentUserAgent = currentWebView.getSettings().getUserAgentString();
-
-            // Get the current font size from the
-            fontSize = currentWebView.getSettings().getTextZoom();
-
-            // Set the status of the menu item checkboxes.
-            optionsDomStorageMenuItem.setChecked(currentWebView.getSettings().getDomStorageEnabled());
-            optionsSaveFormDataMenuItem.setChecked(currentWebView.getSettings().getSaveFormData());  // Form data can be removed once the minimum API >= 26.
-            optionsEasyListMenuItem.setChecked(currentWebView.getEasyListEnabled());
-            optionsEasyPrivacyMenuItem.setChecked(currentWebView.getEasyPrivacyEnabled());
-            optionsFanboysAnnoyanceListMenuItem.setChecked(currentWebView.getFanboysAnnoyanceListEnabled());
-            optionsFanboysSocialBlockingListMenuItem.setChecked(currentWebView.getFanboysSocialBlockingListEnabled());
-            optionsUltraListMenuItem.setChecked(currentWebView.getUltraListEnabled());
-            optionsUltraPrivacyMenuItem.setChecked(currentWebView.getUltraPrivacyEnabled());
-            optionsBlockAllThirdPartyRequestsMenuItem.setChecked(currentWebView.getBlockAllThirdPartyRequests());
-            optionsSwipeToRefreshMenuItem.setChecked(currentWebView.getSwipeToRefresh());
-            optionsWideViewportMenuItem.setChecked(currentWebView.getSettings().getUseWideViewPort());
-            optionsDisplayImagesMenuItem.setChecked(currentWebView.getSettings().getLoadsImagesAutomatically());
-
-            // Initialize the display names for the blocklists with the number of blocked requests.
-            optionsBlocklistsMenuItem.setTitle(getString(R.string.blocklists) + " - " + currentWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
-            optionsEasyListMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.EASYLIST) + " - " + getString(R.string.easylist));
-            optionsEasyPrivacyMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.EASYPRIVACY) + " - " + getString(R.string.easyprivacy));
-            optionsFanboysAnnoyanceListMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST) + " - " + getString(R.string.fanboys_annoyance_list));
-            optionsFanboysSocialBlockingListMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST) + " - " + getString(R.string.fanboys_social_blocking_list));
-            optionsUltraListMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.ULTRALIST) + " - " + getString(R.string.ultralist));
-            optionsUltraPrivacyMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.ULTRAPRIVACY) + " - " + getString(R.string.ultraprivacy));
-            optionsBlockAllThirdPartyRequestsMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.THIRD_PARTY_REQUESTS) + " - " + getString(R.string.block_all_third_party_requests));
-
-            // Enable DOM Storage if JavaScript is enabled.
-            optionsDomStorageMenuItem.setEnabled(currentWebView.getSettings().getJavaScriptEnabled());
-
-            // Get the current theme status.
-            int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
-
-            // Enable dark WebView if night mode is enabled.
-            optionsDarkWebViewMenuItem.setEnabled(currentThemeStatus == Configuration.UI_MODE_NIGHT_YES);
-
-            // Set the checkbox status for dark WebView if the device is running API >= 29 and algorithmic darkening is supported.
-            if ((Build.VERSION.SDK_INT >= 29) && WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING))
-                optionsDarkWebViewMenuItem.setChecked(WebSettingsCompat.isAlgorithmicDarkeningAllowed(currentWebView.getSettings()));
-        }
-
-        // Set the cookies menu item checked status.
-        optionsCookiesMenuItem.setChecked(cookieManager.acceptCookie());
-
-        // Enable Clear Cookies if there are any.
-        optionsClearCookiesMenuItem.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;
-        if (localStorageDirectory.exists()) {
-            // `Objects.requireNonNull` removes a lint warning that `localStorageDirectory.list` might produce a null pointed exception if it is dereferenced.
-            localStorageDirectoryNumberOfFiles = Objects.requireNonNull(localStorageDirectory.list()).length;
-        }
-
-        // Get a count of the number of files in the IndexedDB directory.
-        File indexedDBDirectory = new File (privateDataDirectoryString + "/app_webview/IndexedDB");
-        int indexedDBDirectoryNumberOfFiles = 0;
-        if (indexedDBDirectory.exists()) {
-            // `Objects.requireNonNull` removes a lint warning that `indexedDBDirectory.list` might produce a null pointed exception if it is dereferenced.
-            indexedDBDirectoryNumberOfFiles = Objects.requireNonNull(indexedDBDirectory.list()).length;
-        }
-
-        // Enable Clear DOM Storage if there is any.
-        optionsClearDomStorageMenuItem.setEnabled(localStorageDirectoryNumberOfFiles > 0 || indexedDBDirectoryNumberOfFiles > 0);
-
-        // Enable Clear Form Data is there is any.  This can be removed once the minimum API >= 26.
-        if (Build.VERSION.SDK_INT < 26) {
-            // Get the WebView database.
-            WebViewDatabase webViewDatabase = WebViewDatabase.getInstance(this);
-
-            // Enable the clear form data menu item if there is anything to clear.
-            optionsClearFormDataMenuItem.setEnabled(webViewDatabase.hasFormData());
-        }
-
-        // Enable Clear Data if any of the submenu items are enabled.
-        optionsClearDataMenuItem.setEnabled(optionsClearCookiesMenuItem.isEnabled() || optionsClearDomStorageMenuItem.isEnabled() || optionsClearFormDataMenuItem.isEnabled());
-
-        // Disable Fanboy's Social Blocking List menu item if Fanboy's Annoyance List is checked.
-        optionsFanboysSocialBlockingListMenuItem.setEnabled(!optionsFanboysAnnoyanceListMenuItem.isChecked());
-
-        // Set the proxy title and check the applied proxy.
-        switch (proxyMode) {
-            case ProxyHelper.NONE:
-                // Set the proxy title.
-                optionsProxyMenuItem.setTitle(getString(R.string.proxy) + " - " + getString(R.string.proxy_none));
-
-                // Check the proxy None radio button.
-                optionsProxyNoneMenuItem.setChecked(true);
-                break;
-
-            case ProxyHelper.TOR:
-                // Set the proxy title.
-                optionsProxyMenuItem.setTitle(getString(R.string.proxy) + " - " + getString(R.string.proxy_tor));
-
-                // Check the proxy Tor radio button.
-                optionsProxyTorMenuItem.setChecked(true);
-                break;
-
-            case ProxyHelper.I2P:
-                // Set the proxy title.
-                optionsProxyMenuItem.setTitle(getString(R.string.proxy) + " - " + getString(R.string.proxy_i2p));
-
-                // Check the proxy I2P radio button.
-                optionsProxyI2pMenuItem.setChecked(true);
-                break;
-
-            case ProxyHelper.CUSTOM:
-                // Set the proxy title.
-                optionsProxyMenuItem.setTitle(getString(R.string.proxy) + " - " + getString(R.string.proxy_custom));
-
-                // Check the proxy Custom radio button.
-                optionsProxyCustomMenuItem.setChecked(true);
-                break;
-        }
-
-        // Select the current user agent menu item.  A switch statement cannot be used because the user agents are not compile time constants.
-        if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[0])) {  // Privacy Browser.
-            // Update the user agent menu item title.
-            optionsUserAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_privacy_browser));
-
-            // Select the Privacy Browser radio box.
-            optionsUserAgentPrivacyBrowserMenuItem.setChecked(true);
-        } else if (currentUserAgent.equals(webViewDefaultUserAgent)) {  // WebView Default.
-            // Update the user agent menu item title.
-            optionsUserAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_webview_default));
-
-            // Select the WebView Default radio box.
-            optionsUserAgentWebViewDefaultMenuItem.setChecked(true);
-        } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[2])) {  // Firefox on Android.
-            // Update the user agent menu item title.
-            optionsUserAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_firefox_on_android));
-
-            // Select the Firefox on Android radio box.
-            optionsUserAgentFirefoxOnAndroidMenuItem.setChecked(true);
-        } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[3])) {  // Chrome on Android.
-            // Update the user agent menu item title.
-            optionsUserAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_chrome_on_android));
-
-            // Select the Chrome on Android radio box.
-            optionsUserAgentChromeOnAndroidMenuItem.setChecked(true);
-        } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[4])) {  // Safari on iOS.
-            // Update the user agent menu item title.
-            optionsUserAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_safari_on_ios));
-
-            // Select the Safari on iOS radio box.
-            optionsUserAgentSafariOnIosMenuItem.setChecked(true);
-        } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[5])) {  // Firefox on Linux.
-            // Update the user agent menu item title.
-            optionsUserAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_firefox_on_linux));
-
-            // Select the Firefox on Linux radio box.
-            optionsUserAgentFirefoxOnLinuxMenuItem.setChecked(true);
-        } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[6])) {  // Chromium on Linux.
-            // Update the user agent menu item title.
-            optionsUserAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_chromium_on_linux));
-
-            // Select the Chromium on Linux radio box.
-            optionsUserAgentChromiumOnLinuxMenuItem.setChecked(true);
-        } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[7])) {  // Firefox on Windows.
-            // Update the user agent menu item title.
-            optionsUserAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_firefox_on_windows));
-
-            // Select the Firefox on Windows radio box.
-            optionsUserAgentFirefoxOnWindowsMenuItem.setChecked(true);
-        } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[8])) {  // Chrome on Windows.
-            // Update the user agent menu item title.
-            optionsUserAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_chrome_on_windows));
-
-            // Select the Chrome on Windows radio box.
-            optionsUserAgentChromeOnWindowsMenuItem.setChecked(true);
-        } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[9])) {  // Edge on Windows.
-            // Update the user agent menu item title.
-            optionsUserAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_edge_on_windows));
-
-            // Select the Edge on Windows radio box.
-            optionsUserAgentEdgeOnWindowsMenuItem.setChecked(true);
-        } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[10])) {  // Internet Explorer on Windows.
-            // Update the user agent menu item title.
-            optionsUserAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_internet_explorer_on_windows));
-
-            // Select the Internet on Windows radio box.
-            optionsUserAgentInternetExplorerOnWindowsMenuItem.setChecked(true);
-        } else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[11])) {  // Safari on macOS.
-            // Update the user agent menu item title.
-            optionsUserAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_safari_on_macos));
-
-            // Select the Safari on macOS radio box.
-            optionsUserAgentSafariOnMacosMenuItem.setChecked(true);
-        } else {  // Custom user agent.
-            // Update the user agent menu item title.
-            optionsUserAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_custom));
-
-            // Select the Custom radio box.
-            optionsUserAgentCustomMenuItem.setChecked(true);
-        }
-
-        // Set the font size title.
-        optionsFontSizeMenuItem.setTitle(getString(R.string.font_size) + " - " + fontSize + "%");
-
-        // Run all the other default commands.
-        super.onPrepareOptionsMenu(menu);
-
-        // Display the menu.
-        return true;
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem menuItem) {
-        // Get a handle for the shared preferences.
-        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
-
-        // Get a handle for the cookie manager.
-        CookieManager cookieManager = CookieManager.getInstance();
-
-        // Get the selected menu item ID.
-        int menuItemId = menuItem.getItemId();
-
-        // Run the commands that correlate to the selected menu item.
-        if (menuItemId == R.id.javascript) {  // JavaScript.
-            // Toggle the JavaScript status.
-            currentWebView.getSettings().setJavaScriptEnabled(!currentWebView.getSettings().getJavaScriptEnabled());
-
-            // Update the privacy icon.
-            updatePrivacyIcons(true);
-
-            // Display a `Snackbar`.
-            if (currentWebView.getSettings().getJavaScriptEnabled()) {  // JavaScrip is enabled.
-                Snackbar.make(webViewPager, R.string.javascript_enabled, Snackbar.LENGTH_SHORT).show();
-            } else if (cookieManager.acceptCookie()) {  // JavaScript is disabled, but first-party cookies are enabled.
-                Snackbar.make(webViewPager, R.string.javascript_disabled, Snackbar.LENGTH_SHORT).show();
-            } else {  // Privacy mode.
-                Snackbar.make(webViewPager, R.string.privacy_mode, Snackbar.LENGTH_SHORT).show();
-            }
-
-            // Reload the current WebView.
-            currentWebView.reload();
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.refresh) {  // Refresh.
-            // Run the command that correlates to the current status of the menu item.
-            if (menuItem.getTitle().equals(getString(R.string.refresh))) {  // The refresh button was pushed.
-                // Reload the current WebView.
-                currentWebView.reload();
-            } else {  // The stop button was pushed.
-                // Stop the loading of the WebView.
-                currentWebView.stopLoading();
-            }
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.bookmarks) {  // Bookmarks.
-            // Open the bookmarks drawer.
-            drawerLayout.openDrawer(GravityCompat.END);
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.cookies) {  // Cookies.
-            // Switch the first-party cookie status.
-            cookieManager.setAcceptCookie(!cookieManager.acceptCookie());
-
-            // Store the cookie status.
-            currentWebView.setAcceptCookies(cookieManager.acceptCookie());
-
-            // Update the menu checkbox.
-            menuItem.setChecked(cookieManager.acceptCookie());
-
-            // Update the privacy icon.
-            updatePrivacyIcons(true);
-
-            // Display a snackbar.
-            if (cookieManager.acceptCookie()) {  // Cookies are enabled.
-                Snackbar.make(webViewPager, R.string.cookies_enabled, Snackbar.LENGTH_SHORT).show();
-            } else if (currentWebView.getSettings().getJavaScriptEnabled()) {  // JavaScript is still enabled.
-                Snackbar.make(webViewPager, R.string.cookies_disabled, Snackbar.LENGTH_SHORT).show();
-            } else {  // Privacy mode.
-                Snackbar.make(webViewPager, R.string.privacy_mode, Snackbar.LENGTH_SHORT).show();
-            }
-
-            // Reload the current WebView.
-            currentWebView.reload();
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.dom_storage) {  // DOM storage.
-            // Toggle the status of domStorageEnabled.
-            currentWebView.getSettings().setDomStorageEnabled(!currentWebView.getSettings().getDomStorageEnabled());
-
-            // Update the menu checkbox.
-            menuItem.setChecked(currentWebView.getSettings().getDomStorageEnabled());
-
-            // Update the privacy icon.
-            updatePrivacyIcons(true);
-
-            // Display a snackbar.
-            if (currentWebView.getSettings().getDomStorageEnabled()) {
-                Snackbar.make(webViewPager, R.string.dom_storage_enabled, Snackbar.LENGTH_SHORT).show();
-            } else {
-                Snackbar.make(webViewPager, R.string.dom_storage_disabled, Snackbar.LENGTH_SHORT).show();
-            }
-
-            // Reload the current WebView.
-            currentWebView.reload();
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.save_form_data) {  // Form data.  This can be removed once the minimum API >= 26.
-            // Switch the status of saveFormDataEnabled.
-            currentWebView.getSettings().setSaveFormData(!currentWebView.getSettings().getSaveFormData());
-
-            // Update the menu checkbox.
-            menuItem.setChecked(currentWebView.getSettings().getSaveFormData());
-
-            // Display a snackbar.
-            if (currentWebView.getSettings().getSaveFormData()) {
-                Snackbar.make(webViewPager, R.string.form_data_enabled, Snackbar.LENGTH_SHORT).show();
-            } else {
-                Snackbar.make(webViewPager, R.string.form_data_disabled, Snackbar.LENGTH_SHORT).show();
-            }
-
-            // Update the privacy icon.
-            updatePrivacyIcons(true);
-
-            // Reload the current WebView.
-            currentWebView.reload();
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.clear_cookies) {  // Clear cookies.
-            // Create a snackbar.
-            Snackbar.make(webViewPager, R.string.cookies_deleted, Snackbar.LENGTH_LONG)
-                    .setAction(R.string.undo, v -> {
-                        // Do nothing because everything will be handled by `onDismissed()` below.
-                    })
-                    .addCallback(new Snackbar.Callback() {
-                        @Override
-                        public void onDismissed(Snackbar snackbar, int event) {
-                            if (event != Snackbar.Callback.DISMISS_EVENT_ACTION) {  // The snackbar was dismissed without the undo button being pushed.
-                                // Delete the cookies.
-                                cookieManager.removeAllCookies(null);
-                            }
-                        }
-                    })
-                    .show();
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.clear_dom_storage) {  // Clear DOM storage.
-            // Create a snackbar.
-            Snackbar.make(webViewPager, R.string.dom_storage_deleted, Snackbar.LENGTH_LONG)
-                    .setAction(R.string.undo, v -> {
-                        // Do nothing because everything will be handled by `onDismissed()` below.
-                    })
-                    .addCallback(new Snackbar.Callback() {
-                        @Override
-                        public void onDismissed(Snackbar snackbar, int event) {
-                            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);
-                            }
-                        }
-                    })
-                    .show();
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.clear_form_data) {  // Clear form data.  This can be remove once the minimum API >= 26.
-            // Create a snackbar.
-            Snackbar.make(webViewPager, R.string.form_data_deleted, Snackbar.LENGTH_LONG)
-                    .setAction(R.string.undo, v -> {
-                        // Do nothing because everything will be handled by `onDismissed()` below.
-                    })
-                    .addCallback(new Snackbar.Callback() {
-                        @Override
-                        public void onDismissed(Snackbar snackbar, int event) {
-                            if (event != Snackbar.Callback.DISMISS_EVENT_ACTION) {  // The snackbar was dismissed without the undo button being pushed.
-                                // Get a handle for the webView database.
-                                WebViewDatabase webViewDatabase = WebViewDatabase.getInstance(getApplicationContext());
-
-                                // Delete the form data.
-                                webViewDatabase.clearFormData();
-                            }
-                        }
-                    })
-                    .show();
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.easylist) {  // EasyList.
-            // Toggle the EasyList status.
-            currentWebView.setEasyListEnabled(!currentWebView.getEasyListEnabled());
-
-            // Update the menu checkbox.
-            menuItem.setChecked(currentWebView.getEasyListEnabled());
-
-            // Reload the current WebView.
-            currentWebView.reload();
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.easyprivacy) {  // EasyPrivacy.
-            // Toggle the EasyPrivacy status.
-            currentWebView.setEasyPrivacyEnabled(!currentWebView.getEasyPrivacyEnabled());
-
-            // Update the menu checkbox.
-            menuItem.setChecked(currentWebView.getEasyPrivacyEnabled());
-
-            // Reload the current WebView.
-            currentWebView.reload();
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.fanboys_annoyance_list) {  // Fanboy's Annoyance List.
-            // Toggle Fanboy's Annoyance List status.
-            currentWebView.setFanboysAnnoyanceListEnabled(!currentWebView.getFanboysAnnoyanceListEnabled());
-
-            // Update the menu checkbox.
-            menuItem.setChecked(currentWebView.getFanboysAnnoyanceListEnabled());
-
-            // Update the status of Fanboy's Social Blocking List.
-            optionsFanboysSocialBlockingListMenuItem.setEnabled(!currentWebView.getFanboysAnnoyanceListEnabled());
-
-            // Reload the current WebView.
-            currentWebView.reload();
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.fanboys_social_blocking_list) {  // Fanboy's Social Blocking List.
-            // Toggle Fanboy's Social Blocking List status.
-            currentWebView.setFanboysSocialBlockingListEnabled(!currentWebView.getFanboysSocialBlockingListEnabled());
-
-            // Update the menu checkbox.
-            menuItem.setChecked(currentWebView.getFanboysSocialBlockingListEnabled());
-
-            // Reload the current WebView.
-            currentWebView.reload();
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.ultralist) {  // UltraList.
-            // Toggle the UltraList status.
-            currentWebView.setUltraListEnabled(!currentWebView.getUltraListEnabled());
-
-            // Update the menu checkbox.
-            menuItem.setChecked(currentWebView.getUltraListEnabled());
-
-            // Reload the current WebView.
-            currentWebView.reload();
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.ultraprivacy) {  // UltraPrivacy.
-            // Toggle the UltraPrivacy status.
-            currentWebView.setUltraPrivacyEnabled(!currentWebView.getUltraPrivacyEnabled());
-
-            // Update the menu checkbox.
-            menuItem.setChecked(currentWebView.getUltraPrivacyEnabled());
-
-            // Reload the current WebView.
-            currentWebView.reload();
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.block_all_third_party_requests) {  // Block all third-party requests.
-            //Toggle the third-party requests blocker status.
-            currentWebView.setBlockAllThirdPartyRequests(!currentWebView.getBlockAllThirdPartyRequests());
-
-            // Update the menu checkbox.
-            menuItem.setChecked(currentWebView.getBlockAllThirdPartyRequests());
-
-            // Reload the current WebView.
-            currentWebView.reload();
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.proxy_none) {  // Proxy - None.
-            // Update the proxy mode.
-            proxyMode = ProxyHelper.NONE;
-
-            // Apply the proxy mode.
-            applyProxy(true);
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.proxy_tor) {  // Proxy - Tor.
-            // Update the proxy mode.
-            proxyMode = ProxyHelper.TOR;
-
-            // Apply the proxy mode.
-            applyProxy(true);
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.proxy_i2p) {  // Proxy - I2P.
-            // Update the proxy mode.
-            proxyMode = ProxyHelper.I2P;
-
-            // Apply the proxy mode.
-            applyProxy(true);
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.proxy_custom) {  // Proxy - Custom.
-            // Update the proxy mode.
-            proxyMode = ProxyHelper.CUSTOM;
-
-            // Apply the proxy mode.
-            applyProxy(true);
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.user_agent_privacy_browser) {  // User Agent - Privacy Browser.
-            // Update the user agent.
-            currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[0]);
-
-            // Reload the current WebView.
-            currentWebView.reload();
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.user_agent_webview_default) {  // User Agent - WebView Default.
-            // Update the user agent.
-            currentWebView.getSettings().setUserAgentString("");
-
-            // Reload the current WebView.
-            currentWebView.reload();
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.user_agent_firefox_on_android) {  // User Agent - Firefox on Android.
-            // Update the user agent.
-            currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[2]);
-
-            // Reload the current WebView.
-            currentWebView.reload();
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.user_agent_chrome_on_android) {  // User Agent - Chrome on Android.
-            // Update the user agent.
-            currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[3]);
-
-            // Reload the current WebView.
-            currentWebView.reload();
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.user_agent_safari_on_ios) {  // User Agent - Safari on iOS.
-            // Update the user agent.
-            currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[4]);
-
-            // Reload the current WebView.
-            currentWebView.reload();
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.user_agent_firefox_on_linux) {  // User Agent - Firefox on Linux.
-            // Update the user agent.
-            currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[5]);
-
-            // Reload the current WebView.
-            currentWebView.reload();
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.user_agent_chromium_on_linux) {  // User Agent - Chromium on Linux.
-            // Update the user agent.
-            currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[6]);
-
-            // Reload the current WebView.
-            currentWebView.reload();
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.user_agent_firefox_on_windows) {  // User Agent - Firefox on Windows.
-            // Update the user agent.
-            currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[7]);
-
-            // Reload the current WebView.
-            currentWebView.reload();
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.user_agent_chrome_on_windows) {  // User Agent - Chrome on Windows.
-            // Update the user agent.
-            currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[8]);
-
-            // Reload the current WebView.
-            currentWebView.reload();
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.user_agent_edge_on_windows) {  // User Agent - Edge on Windows.
-            // Update the user agent.
-            currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[9]);
-
-            // Reload the current WebView.
-            currentWebView.reload();
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.user_agent_internet_explorer_on_windows) {  // User Agent - Internet Explorer on Windows.
-            // Update the user agent.
-            currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[10]);
-
-            // Reload the current WebView.
-            currentWebView.reload();
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.user_agent_safari_on_macos) {  // User Agent - Safari on macOS.
-            // Update the user agent.
-            currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[11]);
-
-            // Reload the current WebView.
-            currentWebView.reload();
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.user_agent_custom) {  // User Agent - Custom.
-            // Update the user agent.
-            currentWebView.getSettings().setUserAgentString(sharedPreferences.getString(getString(R.string.custom_user_agent_key), getString(R.string.custom_user_agent_default_value)));
-
-            // Reload the current WebView.
-            currentWebView.reload();
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.font_size) {  // Font size.
-            // Instantiate the font size dialog.
-            DialogFragment fontSizeDialogFragment = FontSizeDialog.displayDialog(currentWebView.getSettings().getTextZoom());
-
-            // Show the font size dialog.
-            fontSizeDialogFragment.show(getSupportFragmentManager(), getString(R.string.font_size));
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.swipe_to_refresh) {  // Swipe to refresh.
-            // Toggle the stored status of swipe to refresh.
-            currentWebView.setSwipeToRefresh(!currentWebView.getSwipeToRefresh());
-
-            // Update the swipe refresh layout.
-            if (currentWebView.getSwipeToRefresh()) {  // Swipe to refresh is enabled.
-                // Only enable the swipe refresh layout if the WebView is scrolled to the top.  It is updated every time the scroll changes.
-                swipeRefreshLayout.setEnabled(currentWebView.getScrollY() == 0);
-            } else {  // Swipe to refresh is disabled.
-                // Disable the swipe refresh layout.
-                swipeRefreshLayout.setEnabled(false);
-            }
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.wide_viewport) {  // Wide viewport.
-            // Toggle the viewport.
-            currentWebView.getSettings().setUseWideViewPort(!currentWebView.getSettings().getUseWideViewPort());
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.display_images) {  // Display images.
-            // Toggle the displaying of images.
-            if (currentWebView.getSettings().getLoadsImagesAutomatically()) {  // Images are currently loaded automatically.
-                // Disable loading of images.
-                currentWebView.getSettings().setLoadsImagesAutomatically(false);
-
-                // Reload the website to remove existing images.
-                currentWebView.reload();
-            } else {  // Images are not currently loaded automatically.
-                // Enable loading of images.  Missing images will be loaded without the need for a reload.
-                currentWebView.getSettings().setLoadsImagesAutomatically(true);
-            }
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.dark_webview) {  // Dark WebView.
-            // Toggle dark WebView if supported.
-            if ((Build.VERSION.SDK_INT >= 29) && WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING))
-                WebSettingsCompat.setAlgorithmicDarkeningAllowed(currentWebView.getSettings(), !WebSettingsCompat.isAlgorithmicDarkeningAllowed(currentWebView.getSettings()));
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.find_on_page) {  // Find on page.
-            // Get a handle for the views.
-            Toolbar toolbar = findViewById(R.id.toolbar);
-            LinearLayout findOnPageLinearLayout = findViewById(R.id.find_on_page_linearlayout);
-            EditText findOnPageEditText = findViewById(R.id.find_on_page_edittext);
-
-            // Set the minimum height of the find on page linear layout to match the toolbar.
-            findOnPageLinearLayout.setMinimumHeight(toolbar.getHeight());
-
-            // Hide the toolbar.
-            toolbar.setVisibility(View.GONE);
-
-            // Show the find on page linear layout.
-            findOnPageLinearLayout.setVisibility(View.VISIBLE);
-
-            // Display the keyboard.  The app must wait 200 ms before running the command to work around a bug in Android.
-            // http://stackoverflow.com/questions/5520085/android-show-softkeyboard-with-showsoftinput-is-not-working
-            findOnPageEditText.postDelayed(() -> {
-                // Set the focus on the find on page edit text.
-                findOnPageEditText.requestFocus();
-
-                // 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;
-
-                // Display the keyboard.  `0` sets no input flags.
-                inputMethodManager.showSoftInput(findOnPageEditText, 0);
-            }, 200);
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.print) {  // Print.
-            // Get a print manager instance.
-            PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE);
-
-            // Remove the lint error below that print manager might be null.
-            assert printManager != null;
-
-            // Create a print document adapter from the current WebView.
-            PrintDocumentAdapter printDocumentAdapter = currentWebView.createPrintDocumentAdapter(getString(R.string.print));
-
-            // Print the document.
-            printManager.print(getString(R.string.privacy_browser_webpage), printDocumentAdapter, null);
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.save_url) {  // Save URL.
-            // Check the download preference.
-            if (downloadWithExternalApp) {  // Download with an external app.
-                downloadUrlWithExternalApp(currentWebView.getCurrentUrl());
-            } else {  // Handle the download inside of Privacy Browser.
-                // Prepare the save dialog.  The dialog will be displayed once the file size and the content disposition have been acquired.
-                PrepareSaveDialogCoroutine.prepareSaveDialog(this, getSupportFragmentManager(), currentWebView.getCurrentUrl(), currentWebView.getSettings().getUserAgentString(),
-                        currentWebView.getAcceptCookies());
-            }
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.save_archive) {
-            // Open the file picker with a default file name built from the current domain name.
-            saveWebpageArchiveActivityResultLauncher.launch(currentWebView.getCurrentDomainName() + ".mht");
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.save_image) {  // Save image.
-            // Open the file picker with a default file name built from the current domain name.
-            saveWebpageImageActivityResultLauncher.launch(currentWebView.getCurrentDomainName() + ".png");
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.add_to_homescreen) {  // Add to homescreen.
-            // Instantiate the create home screen shortcut dialog.
-            DialogFragment createHomeScreenShortcutDialogFragment = CreateHomeScreenShortcutDialog.createDialog(currentWebView.getTitle(), currentWebView.getUrl(),
-                    currentWebView.getFavoriteIcon());
-
-            // Show the create home screen shortcut dialog.
-            createHomeScreenShortcutDialogFragment.show(getSupportFragmentManager(), getString(R.string.create_shortcut));
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.view_source) {  // View source.
-            // Create an intent to launch the view source activity.
-            Intent viewSourceIntent = new Intent(this, ViewSourceActivity.class);
-
-            // Add the variables to the intent.
-            viewSourceIntent.putExtra(ViewSourceActivityKt.CURRENT_URL, currentWebView.getUrl());
-            viewSourceIntent.putExtra(ViewSourceActivityKt.USER_AGENT, currentWebView.getSettings().getUserAgentString());
-
-            // Make it so.
-            startActivity(viewSourceIntent);
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.share_message) {  // Share a message.
-            // Prepare the share string.
-            String shareString = currentWebView.getTitle() + " – " + currentWebView.getUrl();
-
-            // Create the share intent.
-            Intent shareMessageIntent = new Intent(Intent.ACTION_SEND);
-
-            // Add the share string to the intent.
-            shareMessageIntent.putExtra(Intent.EXTRA_TEXT, shareString);
-
-            // Set the MIME type.
-            shareMessageIntent.setType("text/plain");
-
-            // Set the intent to open in a new task.
-            shareMessageIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
-            // Make it so.
-            startActivity(Intent.createChooser(shareMessageIntent, getString(R.string.share_message)));
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.share_url) {  // Share URL.
-            // Create the share intent.
-            Intent shareUrlIntent = new Intent(Intent.ACTION_SEND);
-
-            // Add the URL to the intent.
-            shareUrlIntent.putExtra(Intent.EXTRA_TEXT, currentWebView.getUrl());
-
-            // Add the title to the intent.
-            shareUrlIntent.putExtra(Intent.EXTRA_SUBJECT, currentWebView.getTitle());
-
-            // Set the MIME type.
-            shareUrlIntent.setType("text/plain");
-
-            // Set the intent to open in a new task.
-            shareUrlIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
-            //Make it so.
-            startActivity(Intent.createChooser(shareUrlIntent, getString(R.string.share_url)));
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.open_with_app) {  // Open with app.
-            // Open the URL with an outside app.
-            openWithApp(currentWebView.getUrl());
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.open_with_browser) {  // Open with browser.
-            // Open the URL with an outside browser.
-            openWithBrowser(currentWebView.getUrl());
-
-            // Consume the event.
-            return true;
-        } else if (menuItemId == R.id.add_or_edit_domain) {  // Add or edit domain.
-            // Reapply the domain settings on returning to `MainWebViewActivity`.
-            reapplyDomainSettingsOnRestart = true;
-
-            // Check if domain settings currently exist.
-            if (currentWebView.getDomainSettingsApplied()) {  // Edit the current domain settings.
-                // Create an intent to launch the domains activity.
-                Intent domainsIntent = new Intent(this, DomainsActivity.class);
-
-                // Add the extra information to the intent.
-                domainsIntent.putExtra(DomainsActivity.LOAD_DOMAIN, currentWebView.getDomainSettingsDatabaseId());
-                domainsIntent.putExtra(DomainsActivity.CLOSE_ON_BACK, true);
-                domainsIntent.putExtra(DomainsActivity.CURRENT_URL, currentWebView.getUrl());
-                domainsIntent.putExtra(DomainsActivity.CURRENT_IP_ADDRESSES, currentWebView.getCurrentIpAddresses());
-
-                // Get the current certificate.
-                SslCertificate sslCertificate = currentWebView.getCertificate();
-
-                // Check to see if the SSL certificate is populated.
-                if (sslCertificate != null) {
-                    // Extract the certificate to strings.
-                    String issuedToCName = sslCertificate.getIssuedTo().getCName();
-                    String issuedToOName = sslCertificate.getIssuedTo().getOName();
-                    String issuedToUName = sslCertificate.getIssuedTo().getUName();
-                    String issuedByCName = sslCertificate.getIssuedBy().getCName();
-                    String issuedByOName = sslCertificate.getIssuedBy().getOName();
-                    String issuedByUName = sslCertificate.getIssuedBy().getUName();
-                    long startDateLong = sslCertificate.getValidNotBeforeDate().getTime();
-                    long endDateLong = sslCertificate.getValidNotAfterDate().getTime();
-
-                    // Add the certificate to the intent.
-                    domainsIntent.putExtra(DomainsActivity.SSL_ISSUED_TO_CNAME, issuedToCName);
-                    domainsIntent.putExtra(DomainsActivity.SSL_ISSUED_TO_ONAME, issuedToOName);
-                    domainsIntent.putExtra(DomainsActivity.SSL_ISSUED_TO_UNAME, issuedToUName);
-                    domainsIntent.putExtra(DomainsActivity.SSL_ISSUED_BY_CNAME, issuedByCName);
-                    domainsIntent.putExtra(DomainsActivity.SSL_ISSUED_BY_ONAME, issuedByOName);
-                    domainsIntent.putExtra(DomainsActivity.SSL_ISSUED_BY_UNAME, issuedByUName);
-                    domainsIntent.putExtra(DomainsActivity.SSL_START_DATE, startDateLong);
-                    domainsIntent.putExtra(DomainsActivity.SSL_END_DATE, endDateLong);
-                }
-
-                // Make it so.
-                startActivity(domainsIntent);
-            } else {  // Add a new domain.
-                // Get the current URI.
-                Uri currentUri = Uri.parse(currentWebView.getUrl());
-
-                // Get the current domain from the URI.
-                String currentDomain = currentUri.getHost();
-
-                // Set an empty domain if it is null.
-                if (currentDomain == null)
-                    currentDomain = "";
-
-                // Create the domain and store the database ID.
-                int newDomainDatabaseId = domainsDatabaseHelper.addDomain(currentDomain);
-
-                // Create an intent to launch the domains activity.
-                Intent domainsIntent = new Intent(this, DomainsActivity.class);
-
-                // Add the extra information to the intent.
-                domainsIntent.putExtra(DomainsActivity.LOAD_DOMAIN, newDomainDatabaseId);
-                domainsIntent.putExtra(DomainsActivity.CLOSE_ON_BACK, true);
-                domainsIntent.putExtra(DomainsActivity.CURRENT_URL, currentWebView.getUrl());
-                domainsIntent.putExtra(DomainsActivity.CURRENT_IP_ADDRESSES, currentWebView.getCurrentIpAddresses());
-
-                // Get the current certificate.
-                SslCertificate sslCertificate = currentWebView.getCertificate();
-
-                // Check to see if the SSL certificate is populated.
-                if (sslCertificate != null) {
-                    // Extract the certificate to strings.
-                    String issuedToCName = sslCertificate.getIssuedTo().getCName();
-                    String issuedToOName = sslCertificate.getIssuedTo().getOName();
-                    String issuedToUName = sslCertificate.getIssuedTo().getUName();
-                    String issuedByCName = sslCertificate.getIssuedBy().getCName();
-                    String issuedByOName = sslCertificate.getIssuedBy().getOName();
-                    String issuedByUName = sslCertificate.getIssuedBy().getUName();
-                    long startDateLong = sslCertificate.getValidNotBeforeDate().getTime();
-                    long endDateLong = sslCertificate.getValidNotAfterDate().getTime();
-
-                    // Add the certificate to the intent.
-                    domainsIntent.putExtra(DomainsActivity.SSL_ISSUED_TO_CNAME, issuedToCName);
-                    domainsIntent.putExtra(DomainsActivity.SSL_ISSUED_TO_ONAME, issuedToOName);
-                    domainsIntent.putExtra(DomainsActivity.SSL_ISSUED_TO_UNAME, issuedToUName);
-                    domainsIntent.putExtra(DomainsActivity.SSL_ISSUED_BY_CNAME, issuedByCName);
-                    domainsIntent.putExtra(DomainsActivity.SSL_ISSUED_BY_ONAME, issuedByOName);
-                    domainsIntent.putExtra(DomainsActivity.SSL_ISSUED_BY_UNAME, issuedByUName);
-                    domainsIntent.putExtra(DomainsActivity.SSL_START_DATE, startDateLong);
-                    domainsIntent.putExtra(DomainsActivity.SSL_END_DATE, endDateLong);
-                }
-
-                // Make it so.
-                startActivity(domainsIntent);
-            }
-
-            // Consume the event.
-            return true;
-        } else {  // There is no match with the options menu.  Pass the event up to the parent method.
-            // Don't consume the event.
-            return super.onOptionsItemSelected(menuItem);
-        }
-    }
-
-    // removeAllCookies is deprecated, but it is required for API < 21.
-    @Override
-    public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) {
-        // Get a handle for the shared preferences.
-        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
-
-        // Get the menu item ID.
-        int menuItemId = menuItem.getItemId();
-
-        // Run the commands that correspond to the selected menu item.
-        if (menuItemId == R.id.clear_and_exit) {  // Clear and exit.
-            // Clear and exit Privacy Browser.
-            clearAndExit();
-        } else if (menuItemId == R.id.home) {  // Home.
-            // Load the homepage.
-            loadUrl(currentWebView, sharedPreferences.getString("homepage", getString(R.string.homepage_default_value)));
-        } else if (menuItemId == R.id.back) {  // Back.
-            // Check if the WebView can go back.
-            if (curren