]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blobdiff - app/src/main/java/com/stoutner/privacybrowser/activities/DomainsActivity.java
Combine drawable files. https://redmine.stoutner.com/issues/794
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / activities / DomainsActivity.java
index 037ed592f8cf056977defb4d7963f225cf1a82f0..2ef534f1d4286fb54557e8926efcf3b0bf648c82 100644 (file)
@@ -1,20 +1,20 @@
 /*
- * Copyright © 2017-2019 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2017-2022 Soren Stoutner <soren@stoutner.com>.
  *
- * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+ * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
  *
- * Privacy Browser is free software: you can redistribute it and/or modify
+ * 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 is distributed in the hope that it will be useful,
+ * 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.  If not, see <http://www.gnu.org/licenses/>.
+ * along with Privacy Browser Android.  If not, see <http://www.gnu.org/licenses/>.
  */
 
 package com.stoutner.privacybrowser.activities;
@@ -37,16 +37,18 @@ import android.widget.CursorAdapter;
 import android.widget.EditText;
 import android.widget.ListView;
 import android.widget.RadioButton;
+import android.widget.ScrollView;
 import android.widget.Spinner;
-import android.widget.Switch;
 import android.widget.TextView;
 
+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.appcompat.widget.SwitchCompat;
+import androidx.appcompat.widget.Toolbar;
 import androidx.core.app.NavUtils;
 import androidx.fragment.app.DialogFragment;
-import androidx.fragment.app.FragmentManager;  // The AndroidX dialog fragment must be used or an error is produced on API <=22.
+import androidx.fragment.app.FragmentManager;
 
 import com.google.android.material.floatingactionbutton.FloatingActionButton;
 import com.google.android.material.snackbar.Snackbar;
@@ -60,16 +62,11 @@ import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper;
 import java.util.Objects;
 
 public class DomainsActivity extends AppCompatActivity implements AddDomainDialog.AddDomainListener, DomainsListFragment.DismissSnackbarInterface {
-    // `twoPanedMode` is public static so it can be accessed from `DomainsListFragment`.  It is also used in `onCreate()`, `onCreateOptionsMenu()`, and `populateDomainsListView()`.
+    // Define the public static variables.
+    public static int domainsListViewPosition;
     public static boolean twoPanedMode;
-
-    // `databaseId` is public static so it can be accessed from `DomainsListFragment`.  It is also used in `onCreateOptionsMenu()`, `saveDomainSettings()` and `populateDomainsListView()`.
     public static int currentDomainDatabaseId;
-
-    // `deleteMenuItem` is public static so it can be accessed from `DomainsListFragment`.  It is also used in `onCreateOptionsMenu()`, `onOptionsItemSelected()`, and `onBackPressed()`.
     public static MenuItem deleteMenuItem;
-
-    // `dismissingSnackbar` is public static so it can be accessed from `DomainsListFragment`.  It is also used in `onOptionsItemSelected()`.
     public static boolean dismissingSnackbar;
 
     // The SSL certificate and IP address information are accessed from `DomainSettingsFragment` and `saveDomainSettings()`.
@@ -84,6 +81,21 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo
     public static String currentIpAddresses;
 
 
+    // Initialize the class constants.
+    private final String LISTVIEW_POSITION = "listview_position";
+    private final String DOMAIN_SETTINGS_DISPLAYED = "domain_settings_displayed";
+    private final String DOMAIN_SETTINGS_DATABASE_ID = "domain_settings_database_is";
+    private final String DOMAIN_SETTINGS_SCROLL_Y = "domain_settings_scroll_y";
+
+    // Initialize the class variables.
+    private boolean restartAfterRotate;
+    private boolean domainSettingsDisplayedBeforeRotate;
+    private int domainSettingsDatabaseIdBeforeRotate;
+    private int domainSettingsScrollY = 0;
+
+    // Defile the class views.
+    private ListView domainsListView;
+
     // `closeActivityAfterDismissingSnackbar` is used in `onOptionsItemSelected()`, and `onBackPressed()`.
     private boolean closeActivityAfterDismissingSnackbar;
 
@@ -93,24 +105,12 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo
     // `domainsDatabaseHelper` is used in `onCreate()`, `saveDomainSettings()`, and `onDestroy()`.
     private static DomainsDatabaseHelper domainsDatabaseHelper;
 
-    // `domainsListView` is used in `onCreate()` and `populateDomainsList()`.
-    private ListView domainsListView;
-
     // `addDomainFAB` is used in `onCreate()`, `onCreateOptionsMenu()`, `onOptionsItemSelected()`, and `onBackPressed()`.
     private FloatingActionButton addDomainFAB;
 
     // `deletedDomainPosition` is used in an inner and outer class in `onOptionsItemSelected()`.
     private int deletedDomainPosition;
 
-    // `restartAfterRotate` is used in `onCreate()` and `onCreateOptionsMenu()`.
-    private boolean restartAfterRotate;
-
-    // `domainSettingsDisplayedBeforeRotate` is used in `onCreate()` and `onCreateOptionsMenu()`.
-    private boolean domainSettingsDisplayedBeforeRotate;
-
-    // `domainSettingsDatabaseIdBeforeRotate` is used in `onCreate()` and `onCreateOptionsMenu()`.
-    private int domainSettingsDatabaseIdBeforeRotate;
-
     // `goDirectlyToDatabaseId` is used in `onCreate()` and `onCreateOptionsMenu()`.
     private int goDirectlyToDatabaseId;
 
@@ -128,30 +128,31 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo
         // Get a handle for the shared preferences.
         SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
 
-        // Get the theme and screenshot preferences.
-        boolean darkTheme = sharedPreferences.getBoolean("dark_theme", false);
-        boolean allowScreenshots = sharedPreferences.getBoolean("allow_screenshots", false);
+        // 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);
         }
 
-        // Set the activity theme.
-        if (darkTheme) {
-            setTheme(R.style.PrivacyBrowserDark_SecondaryActivity);
-        } else {
-            setTheme(R.style.PrivacyBrowserLight_SecondaryActivity);
-        }
+        // Set the theme.
+        setTheme(R.style.PrivacyBrowser);
 
         // Run the default commands.
         super.onCreate(savedInstanceState);
 
-        // Extract the values from `savedInstanceState` if it is not `null`.
+        // Initialize the domains listview position.
+        domainsListViewPosition = 0;
+
+        // Extract the values from the saved instance state if it is not null.
         if (savedInstanceState != null) {
+            domainsListViewPosition = savedInstanceState.getInt(LISTVIEW_POSITION);
             restartAfterRotate = true;
-            domainSettingsDisplayedBeforeRotate = savedInstanceState.getBoolean("domain_settings_displayed");
-            domainSettingsDatabaseIdBeforeRotate = savedInstanceState.getInt("domain_settings_database_id");
+            domainSettingsDisplayedBeforeRotate = savedInstanceState.getBoolean(DOMAIN_SETTINGS_DISPLAYED);
+            domainSettingsDatabaseIdBeforeRotate = savedInstanceState.getInt(DOMAIN_SETTINGS_DATABASE_ID);
+            domainSettingsScrollY = savedInstanceState.getInt(DOMAIN_SETTINGS_SCROLL_Y);
         }
 
         // Get the launching intent
@@ -177,8 +178,12 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo
         sslEndDateLong = intent.getLongExtra("ssl_end_date", 0);
         currentIpAddresses = intent.getStringExtra("current_ip_addresses");
 
-        // Set the content view.
-        setContentView(R.layout.domains_coordinatorlayout);
+        // Set the view.
+        if (bottomAppBar) {
+            setContentView(R.layout.domains_bottom_appbar);
+        } else {
+            setContentView(R.layout.domains_top_appbar);
+        }
 
         // Populate the class variables.
         coordinatorLayout = findViewById(R.id.domains_coordinatorlayout);
@@ -208,6 +213,9 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo
 
         // Configure the add domain floating action button.
         addDomainFAB.setOnClickListener((View view) -> {
+            // Remove the incorrect warning below that the current URL might be null.
+            assert currentUrl != null;
+
             // Create an add domain dialog.
             DialogFragment addDomainDialog = AddDomainDialog.addDomain(currentUrl);
 
@@ -242,7 +250,7 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo
                 fragmentManager.executePendingTransactions();
 
                 // Populate the list of domains.  `domainSettingsDatabaseId` highlights the domain that was highlighted before the rotation.
-                populateDomainsListView(domainSettingsDatabaseIdBeforeRotate);
+                populateDomainsListView(domainSettingsDatabaseIdBeforeRotate, domainsListViewPosition);
             } else {  // The device is in single-paned mode.
                 // Reset `restartAfterRotate`.
                 restartAfterRotate = false;
@@ -250,21 +258,26 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo
                 // Store the current domain database ID.
                 currentDomainDatabaseId = domainSettingsDatabaseIdBeforeRotate;
 
-                // Add `currentDomainDatabaseId` to `argumentsBundle`.
+                // Create an arguments bundle.
                 Bundle argumentsBundle = new Bundle();
+
+                // Add the domain settings arguments.
                 argumentsBundle.putInt(DomainSettingsFragment.DATABASE_ID, currentDomainDatabaseId);
+                argumentsBundle.putInt(DomainSettingsFragment.SCROLL_Y, domainSettingsScrollY);
 
-                // Add `argumentsBundle` to `domainSettingsFragment`.
+                // Instantiate a new domain settings fragment.
                 DomainSettingsFragment domainSettingsFragment = new DomainSettingsFragment();
+
+                // Add the arguments bundle to the domain settings fragment.
                 domainSettingsFragment.setArguments(argumentsBundle);
 
-                // Show `deleteMenuItem`.
+                // Show the delete menu item.
                 deleteMenuItem.setVisible(true);
 
                 // Hide the add domain floating action button.
                 addDomainFAB.hide();
 
-                // Display `domainSettingsFragment`.
+                // Display the domain settings fragment.
                 fragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainSettingsFragment).commit();
             }
         } else {  // The device was not rotated or, if it was, domain settings were not displayed previously.
@@ -279,23 +292,28 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo
                     fragmentManager.executePendingTransactions();
 
                     // Populate the list of domains.  `domainSettingsDatabaseId` highlights the domain that was highlighted before the rotation.
-                    populateDomainsListView(goDirectlyToDatabaseId);
+                    populateDomainsListView(goDirectlyToDatabaseId, domainsListViewPosition);
                 } else {  // The device is in single-paned mode.
-                    // Add the domain ID to be loaded to `argumentsBundle`.
+                    // Create an arguments bundle.
                     Bundle argumentsBundle = new Bundle();
+
+                    // Add the domain settings to arguments bundle.
                     argumentsBundle.putInt(DomainSettingsFragment.DATABASE_ID, goDirectlyToDatabaseId);
+                    argumentsBundle.putInt(DomainSettingsFragment.SCROLL_Y, domainSettingsScrollY);
 
-                    // Add `argumentsBundle` to `domainSettingsFragment`.
+                    // Instantiate a new domain settings fragment.
                     DomainSettingsFragment domainSettingsFragment = new DomainSettingsFragment();
+
+                    // Add the arguments bundle to the domain settings fragment`.
                     domainSettingsFragment.setArguments(argumentsBundle);
 
-                    // Show `deleteMenuItem`.
+                    // Show the delete menu item.
                     deleteMenuItem.setVisible(true);
 
                     // Hide the add domain floating action button.
                     addDomainFAB.hide();
 
-                    // Display `domainSettingsFragment`.
+                    // Display the domain settings fragment.
                     fragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainSettingsFragment).commit();
                 }
             } else {  // Highlight the first domain.
@@ -305,7 +323,7 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo
                 fragmentManager.executePendingTransactions();
 
                 // Populate the list of domains.  `-1` highlights the first domain.
-                populateDomainsListView(-1);
+                populateDomainsListView(-1, domainsListViewPosition);
             }
         }
 
@@ -316,88 +334,98 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo
     @Override
     public boolean onOptionsItemSelected(MenuItem menuItem) {
         // Get the ID of the menu item that was selected.
-        int menuItemID = menuItem.getItemId();
+        int menuItemId = menuItem.getItemId();
 
         // Get a handle for the fragment manager.
         FragmentManager fragmentManager = getSupportFragmentManager();
 
-        switch (menuItemID) {
-            case android.R.id.home:  // The home arrow is identified as `android.R.id.home`, not just `R.id.home`.
-                if (twoPanedMode) {  // The device is in two-paned mode.
-                    // Save the current domain settings if the domain settings fragment is displayed.
-                    if (findViewById(R.id.domain_settings_scrollview) != null) {
-                        saveDomainSettings(coordinatorLayout, resources);
-                    }
-
-                    // Dismiss the undo delete `SnackBar` if it is shown.
-                    if (undoDeleteSnackbar != null && undoDeleteSnackbar.isShown()) {
-                        // Set the close flag.
-                        closeActivityAfterDismissingSnackbar = true;
-
-                        // Dismiss the snackbar.
-                        undoDeleteSnackbar.dismiss();
-                    } else {
-                        // Go home.
-                        NavUtils.navigateUpFromSameTask(this);
-                    }
-                } else if (closeOnBack) {  // Go directly back to the main WebView activity because the domains activity was launched from the options menu.
-                    // Save the current domain settings.
+        // Run the command according to the selected menu item.
+        if (menuItemId == android.R.id.home) {  // The home arrow is identified as `android.R.id.home`, not just `R.id.home`.
+            // Check if the device is in two-paned mode.
+            if (twoPanedMode) {  // The device is in two-paned mode.
+                // Save the current domain settings if the domain settings fragment is displayed.
+                if (findViewById(R.id.domain_settings_scrollview) != null) {
                     saveDomainSettings(coordinatorLayout, resources);
+                }
 
+                // Dismiss the undo delete snackbar if it is shown.
+                if (undoDeleteSnackbar != null && undoDeleteSnackbar.isShown()) {
+                    // Set the close flag.
+                    closeActivityAfterDismissingSnackbar = true;
+
+                    // Dismiss the snackbar.
+                    undoDeleteSnackbar.dismiss();
+                } else {
                     // Go home.
                     NavUtils.navigateUpFromSameTask(this);
-                } else if (findViewById(R.id.domain_settings_scrollview) != null) {  // The device is in single-paned mode and the domain settings fragment is displayed.
-                    // Save the current domain settings.
-                    saveDomainSettings(coordinatorLayout, resources);
+                }
+            } else if (closeOnBack) {  // Go directly back to the main WebView activity because the domains activity was launched from the options menu.
+                // Save the current domain settings.
+                saveDomainSettings(coordinatorLayout, resources);
 
-                    // Display the domains list fragment.
-                    DomainsListFragment domainsListFragment = new DomainsListFragment();
-                    fragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainsListFragment).commit();
-                    fragmentManager.executePendingTransactions();
+                // Go home.
+                NavUtils.navigateUpFromSameTask(this);
+            } else if (findViewById(R.id.domain_settings_scrollview) != null) {  // The device is in single-paned mode and the domain settings fragment is displayed.
+                // Save the current domain settings.
+                saveDomainSettings(coordinatorLayout, resources);
+
+                // Display the domains list fragment.
+                DomainsListFragment domainsListFragment = new DomainsListFragment();
+                fragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainsListFragment).commit();
+                fragmentManager.executePendingTransactions();
 
-                    // Populate the list of domains.  `-1` highlights the first domain if in two-paned mode.  It has no effect in single-paned mode.
-                    populateDomainsListView(-1);
+                // Populate the list of domains.  `-1` highlights the first domain if in two-paned mode.  It has no effect in single-paned mode.
+                populateDomainsListView(-1, domainsListViewPosition);
 
-                    // Show the add domain floating action button.
-                    addDomainFAB.show();
+                // Show the add domain floating action button.
+                addDomainFAB.show();
 
-                    // Hide the delete menu item.
-                    deleteMenuItem.setVisible(false);
-                } else {  // The device is in single-paned mode and `DomainsListFragment` is displayed.
-                    // Dismiss the undo delete `SnackBar` if it is shown.
-                    if (undoDeleteSnackbar != null && undoDeleteSnackbar.isShown()) {
-                        // Set the close flag.
-                        closeActivityAfterDismissingSnackbar = true;
-
-                        // Dismiss the snackbar.
-                        undoDeleteSnackbar.dismiss();
-                    } else {
-                        // Go home.
-                        NavUtils.navigateUpFromSameTask(this);
-                    }
-                }
-                break;
+                // Hide the delete menu item.
+                deleteMenuItem.setVisible(false);
+            } else {  // The device is in single-paned mode and domains list fragment is displayed.
+                // Dismiss the undo delete `SnackBar` if it is shown.
+                if (undoDeleteSnackbar != null && undoDeleteSnackbar.isShown()) {
+                    // Set the close flag.
+                    closeActivityAfterDismissingSnackbar = true;
 
-            case R.id.delete_domain:
+                    // Dismiss the snackbar.
+                    undoDeleteSnackbar.dismiss();
+                } else {
+                    // Go home.
+                    NavUtils.navigateUpFromSameTask(this);
+                }
+            }
+        } else if (menuItemId == R.id.delete_domain) {  // Delete.
+            // Get a handle for the activity.
+            Activity activity = this;
+
+            // Check to see if the domain settings were loaded directly for editing of this app in single-paned mode.
+            if (closeOnBack && !twoPanedMode) {  // The activity should delete the domain settings and exit straight to the the main WebView activity.
+                // Delete the selected domain.
+                domainsDatabaseHelper.deleteDomain(currentDomainDatabaseId);
+
+                // Go home.
+                NavUtils.navigateUpFromSameTask(activity);
+            } else {  // A snackbar should be shown before deleting the domain settings.
                 // Reset close-on-back, which otherwise can cause errors if the system attempts to save settings for a domain that no longer exists.
                 closeOnBack = false;
 
-                // Store a copy of `currentDomainDatabaseId` because it could change while the `Snackbar` is displayed.
+                // Store a copy of the current domain database ID because it could change while the snackbar is displayed.
                 final int databaseIdToDelete = currentDomainDatabaseId;
 
                 // Update the fragments and menu items.
                 if (twoPanedMode) {  // Two-paned mode.
-                    // Store the deleted domain position, which is needed if `Undo` is selected in the `Snackbar`.
+                    // Store the deleted domain position, which is needed if undo is selected in the snackbar.
                     deletedDomainPosition = domainsListView.getCheckedItemPosition();
 
-                    // Disable the options `MenuItems`.
+                    // Disable the options menu items.
                     deleteMenuItem.setEnabled(false);
-                    deleteMenuItem.setIcon(R.drawable.delete_blue);
+                    deleteMenuItem.setIcon(R.drawable.delete_disabled);
 
                     // Remove the domain settings fragment.
                     fragmentManager.beginTransaction().remove(Objects.requireNonNull(fragmentManager.findFragmentById(R.id.domain_settings_fragment_container))).commit();
                 } else {  // Single-paned mode.
-                    // Display `DomainsListFragment`.
+                    // Display the domains list fragment.
                     DomainsListFragment domainsListFragment = new DomainsListFragment();
                     fragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainsListFragment).commit();
                     fragmentManager.executePendingTransactions();
@@ -409,36 +437,37 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo
                     deleteMenuItem.setVisible(false);
                 }
 
-                // Get a `Cursor` that does not show the domain to be deleted.
+                // Get a cursor that does not show the domain to be deleted.
                 Cursor domainsPendingDeleteCursor = domainsDatabaseHelper.getDomainNameCursorOrderedByDomainExcept(databaseIdToDelete);
 
-                // Setup `domainsPendingDeleteCursorAdapter` with `this` context.  `false` disables `autoRequery`.
+                // Setup the domains pending delete cursor adapter.
                 CursorAdapter domainsPendingDeleteCursorAdapter = new CursorAdapter(this, domainsPendingDeleteCursor, false) {
                     @Override
                     public View newView(Context context, Cursor cursor, ViewGroup parent) {
-                        // Inflate the individual item layout.  `false` does not attach it to the root.
+                        // Inflate the individual item layout.
                         return getLayoutInflater().inflate(R.layout.domain_name_linearlayout, parent, false);
                     }
 
                     @Override
                     public void bindView(View view, Context context, Cursor cursor) {
-                        // Set the domain name.
-                        String domainNameString = cursor.getString(cursor.getColumnIndex(DomainsDatabaseHelper.DOMAIN_NAME));
+                        // Get the domain name string.
+                        String domainNameString = cursor.getString(cursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DOMAIN_NAME));
+
+                        // Get a handle for the domain name text view.
                         TextView domainNameTextView = view.findViewById(R.id.domain_name_textview);
+
+                        // Display the domain name.
                         domainNameTextView.setText(domainNameString);
                     }
                 };
 
-                // Update the handle for the current `domains_listview`.
+                // Update the handle for the current domains list view.
                 domainsListView = findViewById(R.id.domains_listview);
 
-                // Update the `ListView`.
+                // Update the list view.
                 domainsListView.setAdapter(domainsPendingDeleteCursorAdapter);
 
-                // Get a handle for the activity.
-                Activity activity = this;
-
-                // Display a `Snackbar`.
+                // Display a snackbar.
                 undoDeleteSnackbar = Snackbar.make(domainsListView, R.string.domain_deleted, Snackbar.LENGTH_LONG)
                         .setAction(R.string.undo, (View v) -> {
                             // Do nothing because everything will be handled by `onDismissed()` below.
@@ -448,58 +477,70 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo
                             public void onDismissed(Snackbar snackbar, int event) {
                                 // Run commands based on the event.
                                 if (event == Snackbar.Callback.DISMISS_EVENT_ACTION) {  // The user pushed the `Undo` button.
-                                    // Store the database ID in arguments bundle.
+                                    // Create an arguments bundle.
                                     Bundle argumentsBundle = new Bundle();
+
+                                    // Store the domains settings in the arguments bundle.
                                     argumentsBundle.putInt(DomainSettingsFragment.DATABASE_ID, databaseIdToDelete);
+                                    argumentsBundle.putInt(DomainSettingsFragment.SCROLL_Y, domainSettingsScrollY);
 
-                                    // Add the arguments bundle to the domain settings fragment.
+                                    // Instantiate a new domain settings fragment.
                                     DomainSettingsFragment domainSettingsFragment = new DomainSettingsFragment();
+
+                                    // Add the arguments bundle to the domain settings fragment.
                                     domainSettingsFragment.setArguments(argumentsBundle);
 
                                     // Display the correct fragments.
                                     if (twoPanedMode) {  // The device in in two-paned mode.
-                                        // Get a `Cursor` with the current contents of the domains database.
+                                        // Get a cursor with the current contents of the domains database.
                                         Cursor undoDeleteDomainsCursor = domainsDatabaseHelper.getDomainNameCursorOrderedByDomain();
 
-                                        // Setup `domainsCursorAdapter` with `this` context.  `false` disables `autoRequery`.
+                                        // Setup the domains cursor adapter.
                                         CursorAdapter undoDeleteDomainsCursorAdapter = new CursorAdapter(getApplicationContext(), undoDeleteDomainsCursor, false) {
                                             @Override
                                             public View newView(Context context, Cursor cursor, ViewGroup parent) {
-                                                // Inflate the individual item layout.  `false` does not attach it to the root.
+                                                // Inflate the individual item layout.
                                                 return getLayoutInflater().inflate(R.layout.domain_name_linearlayout, parent, false);
                                             }
 
                                             @Override
                                             public void bindView(View view, Context context, Cursor cursor) {
-                                                // Set the domain name.
-                                                String domainNameString = cursor.getString(cursor.getColumnIndex(DomainsDatabaseHelper.DOMAIN_NAME));
+                                                /// Get the domain name string.
+                                                String domainNameString = cursor.getString(cursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DOMAIN_NAME));
+
+                                                // Get a handle for the domain name text view.
                                                 TextView domainNameTextView = view.findViewById(R.id.domain_name_textview);
+
+                                                // Display the domain name.
                                                 domainNameTextView.setText(domainNameString);
                                             }
                                         };
 
-                                        // Update the `ListView`.
+                                        // Update the domains list view.
                                         domainsListView.setAdapter(undoDeleteDomainsCursorAdapter);
-                                        // Select the previously deleted domain in `domainsListView`.
+
+                                        // Select the previously deleted domain in the list view.
                                         domainsListView.setItemChecked(deletedDomainPosition, true);
 
-                                        // Display `domainSettingsFragment`.
+                                        // Display the domain settings fragment.
                                         fragmentManager.beginTransaction().replace(R.id.domain_settings_fragment_container, domainSettingsFragment).commit();
 
-                                        // Enable the options `MenuItems`.
+                                        // Enable the options delete menu item.
                                         deleteMenuItem.setEnabled(true);
-                                        deleteMenuItem.setIcon(R.drawable.delete_light);
+
+                                        // Set the delete menu item icon.
+                                        deleteMenuItem.setIcon(R.drawable.delete_enabled);
                                     } else {  // The device in in one-paned mode.
-                                        // Display `domainSettingsFragment`.
+                                        // Display the domain settings fragment.
                                         fragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainSettingsFragment).commit();
 
                                         // Hide the add domain floating action button.
                                         addDomainFAB.hide();
 
-                                        // Show and enable `deleteMenuItem`.
+                                        // Show and enable the delete menu item.
                                         deleteMenuItem.setVisible(true);
 
-                                        // Display `domainSettingsFragment`.
+                                        // Display the domain settings fragment.
                                         fragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainSettingsFragment).commit();
                                     }
                                 } else {  // The snackbar was dismissed without the undo button being pushed.
@@ -510,29 +551,19 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo
                                     if (dismissingSnackbar) {
                                         // Create a `Runnable` to enable the delete menu item.
                                         Runnable enableDeleteMenuItemRunnable = () -> {
-                                            // Enable `deleteMenuItem` according to the display mode.
+                                            // Enable the delete menu item according to the display mode.
                                             if (twoPanedMode) {  // Two-paned mode.
                                                 // Enable the delete menu item.
                                                 deleteMenuItem.setEnabled(true);
 
-                                                // Get a handle for the shared preferences.
-                                                SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
-
-                                                // Get the theme preferences.
-                                                boolean darkTheme = sharedPreferences.getBoolean("dark_theme", false);
-
-                                                // Set the delete icon according to the theme.
-                                                if (darkTheme) {
-                                                    deleteMenuItem.setIcon(R.drawable.delete_dark);
-                                                } else {
-                                                    deleteMenuItem.setIcon(R.drawable.delete_light);
-                                                }
+                                                // Set the delete menu item icon.
+                                                deleteMenuItem.setIcon(R.drawable.delete_enabled);
                                             } else {  // Single-paned mode.
-                                                // Show `deleteMenuItem`.
+                                                // Show the delete menu item.
                                                 deleteMenuItem.setVisible(true);
                                             }
 
-                                            // Reset `dismissingSnackbar`.
+                                            // Reset the dismissing snackbar tracker.
                                             dismissingSnackbar = false;
                                         };
 
@@ -552,7 +583,7 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo
 
                 // Show the Snackbar.
                 undoDeleteSnackbar.show();
-                break;
+            }
         }
 
         // Consume the event.
@@ -560,21 +591,40 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo
     }
 
     @Override
-    protected void onSaveInstanceState(Bundle outState) {
-        // Store the current `DomainSettingsFragment` state in `outState`.
-        if (findViewById(R.id.domain_settings_scrollview) != null) {  // `DomainSettingsFragment` is displayed.
+    protected void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
+        // Run the default commands.
+        super.onSaveInstanceState(savedInstanceState);
+
+        // Get a handle for the domain settings scrollview.
+        ScrollView domainSettingsScrollView = findViewById(R.id.domain_settings_scrollview);
+
+        // Check to see if the domain settings scrollview exists.
+        if (domainSettingsScrollView == null) {  // The domain settings are not displayed.
+            // Store the domain settings status in the bundle.
+            savedInstanceState.putBoolean(DOMAIN_SETTINGS_DISPLAYED, false);
+            savedInstanceState.putInt(DOMAIN_SETTINGS_DATABASE_ID, -1);
+            savedInstanceState.putInt(DOMAIN_SETTINGS_SCROLL_Y, 0);
+        } else {  // The domain settings are displayed.
             // Save any changes that have been made to the domain settings.
             saveDomainSettings(coordinatorLayout, resources);
 
-            // Store `DomainSettingsDisplayed`.
-            outState.putBoolean("domain_settings_displayed", true);
-            outState.putInt("domain_settings_database_id", DomainSettingsFragment.databaseId);
-        } else {  // `DomainSettingsFragment` is not displayed.
-            outState.putBoolean("domain_settings_displayed", false);
-            outState.putInt("domain_settings_database_id", -1);
+            // Get the domain settings scroll Y.
+            int domainSettingsScrollY = domainSettingsScrollView.getScrollY();
+
+            // Store the domain settings status in the bundle.
+            savedInstanceState.putBoolean(DOMAIN_SETTINGS_DISPLAYED, true);
+            savedInstanceState.putInt(DOMAIN_SETTINGS_DATABASE_ID, DomainSettingsFragment.databaseId);
+            savedInstanceState.putInt(DOMAIN_SETTINGS_SCROLL_Y, domainSettingsScrollY);
         }
 
-        super.onSaveInstanceState(outState);
+        // Check to see if the domains listview exists.
+        if (domainsListView != null) {
+            // Get the domains listview position.
+            int domainsListViewPosition = domainsListView.getFirstVisiblePosition();
+
+            // Store the listview position in the bundle.
+            savedInstanceState.putInt(LISTVIEW_POSITION, domainsListViewPosition);
+        }
     }
 
     // Control what the navigation bar back button does.
@@ -616,7 +666,7 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo
             fragmentManager.executePendingTransactions();
 
             // Populate the list of domains.  `-1` highlights the first domain if in two-paned mode.  It has no effect in single-paned mode.
-            populateDomainsListView(-1);
+            populateDomainsListView(-1, domainsListViewPosition);
 
             // Show the add domain floating action button.
             addDomainFAB.show();
@@ -639,14 +689,19 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo
     }
 
     @Override
-    public void onAddDomain(DialogFragment dialogFragment) {
+    public void onAddDomain(@NonNull DialogFragment dialogFragment) {
         // Dismiss the undo delete snackbar if it is currently displayed.
         if ((undoDeleteSnackbar != null) && undoDeleteSnackbar.isShown()) {
             undoDeleteSnackbar.dismiss();
         }
 
-        // Get the new domain name String from the dialog fragment.
+        // Remove the incorrect lint warning below that the dialog might be null.
+        assert dialogFragment.getDialog() != null;
+
+        // Get a handle for the domain name edit text.
         EditText domainNameEditText = dialogFragment.getDialog().findViewById(R.id.domain_name_edittext);
+
+        // Get the domain name string.
         String domainNameString = domainNameEditText.getText().toString();
 
         // Create the domain and store the database ID in `currentDomainDatabaseId`.
@@ -654,20 +709,25 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo
 
         // Display the newly created domain.
         if (twoPanedMode) {  // The device in in two-paned mode.
-            populateDomainsListView(currentDomainDatabaseId);
+            populateDomainsListView(currentDomainDatabaseId, 0);
         } else {  // The device is in single-paned mode.
             // Hide the add domain floating action button.
             addDomainFAB.hide();
 
-            // Show and enable `deleteMenuItem`.
+            // Show and enable the delete menu item.
             DomainsActivity.deleteMenuItem.setVisible(true);
 
-            // Add the current domain database ID to the arguments bundle.
+            // Create an arguments bundle.
             Bundle argumentsBundle = new Bundle();
+
+            // Add the domain settings to the arguments bundle.  The scroll Y should always be `0` on a new domain.
             argumentsBundle.putInt(DomainSettingsFragment.DATABASE_ID, currentDomainDatabaseId);
+            argumentsBundle.putInt(DomainSettingsFragment.SCROLL_Y, 0);
 
-            // Add and arguments bundle to the domain setting fragment.
+            // Instantiate a new domain settings fragment.
             DomainSettingsFragment domainSettingsFragment = new DomainSettingsFragment();
+
+            // Add the arguments bundle to the domain setting fragment.
             domainSettingsFragment.setArguments(argumentsBundle);
 
             // Display the domain settings fragment.
@@ -678,35 +738,34 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo
     public void saveDomainSettings(View view, Resources resources) {
         // Get handles for the domain settings.
         EditText domainNameEditText = view.findViewById(R.id.domain_settings_name_edittext);
-        Switch javaScriptSwitch = view.findViewById(R.id.javascript_switch);
-        Switch firstPartyCookiesSwitch = view.findViewById(R.id.first_party_cookies_switch);
-        Switch thirdPartyCookiesSwitch = view.findViewById(R.id.third_party_cookies_switch);
-        Switch domStorageSwitch = view.findViewById(R.id.dom_storage_switch);
-        Switch formDataSwitch = view.findViewById(R.id.form_data_switch);  // Form data can be removed once the minimum API >= 26.
-        Switch easyListSwitch = view.findViewById(R.id.easylist_switch);
-        Switch easyPrivacySwitch = view.findViewById(R.id.easyprivacy_switch);
-        Switch fanboysAnnoyanceSwitch = view.findViewById(R.id.fanboys_annoyance_list_switch);
-        Switch fanboysSocialBlockingSwitch = view.findViewById(R.id.fanboys_social_blocking_list_switch);
-        Switch ultraListSwitch = view.findViewById(R.id.ultralist_switch);
-        Switch ultraPrivacySwitch = view.findViewById(R.id.ultraprivacy_switch);
-        Switch blockAllThirdPartyRequestsSwitch = view.findViewById(R.id.block_all_third_party_requests_switch);
+        SwitchCompat javaScriptSwitch = view.findViewById(R.id.javascript_switch);
+        SwitchCompat cookiesSwitch = view.findViewById(R.id.cookies_switch);
+        SwitchCompat domStorageSwitch = view.findViewById(R.id.dom_storage_switch);
+        SwitchCompat formDataSwitch = view.findViewById(R.id.form_data_switch);  // Form data can be removed once the minimum API >= 26.
+        SwitchCompat easyListSwitch = view.findViewById(R.id.easylist_switch);
+        SwitchCompat easyPrivacySwitch = view.findViewById(R.id.easyprivacy_switch);
+        SwitchCompat fanboysAnnoyanceSwitch = view.findViewById(R.id.fanboys_annoyance_list_switch);
+        SwitchCompat fanboysSocialBlockingSwitch = view.findViewById(R.id.fanboys_social_blocking_list_switch);
+        SwitchCompat ultraListSwitch = view.findViewById(R.id.ultralist_switch);
+        SwitchCompat ultraPrivacySwitch = view.findViewById(R.id.ultraprivacy_switch);
+        SwitchCompat blockAllThirdPartyRequestsSwitch = view.findViewById(R.id.block_all_third_party_requests_switch);
         Spinner userAgentSpinner = view.findViewById(R.id.user_agent_spinner);
         EditText customUserAgentEditText = view.findViewById(R.id.custom_user_agent_edittext);
         Spinner fontSizeSpinner = view.findViewById(R.id.font_size_spinner);
+        EditText customFontSizeEditText = view.findViewById(R.id.custom_font_size_edittext);
         Spinner swipeToRefreshSpinner = view.findViewById(R.id.swipe_to_refresh_spinner);
-        Spinner nightModeSpinner = view.findViewById(R.id.night_mode_spinner);
+        Spinner webViewThemeSpinner = view.findViewById(R.id.webview_theme_spinner);
         Spinner wideViewportSpinner = view.findViewById(R.id.wide_viewport_spinner);
         Spinner displayWebpageImagesSpinner = view.findViewById(R.id.display_webpage_images_spinner);
-        Switch pinnedSslCertificateSwitch = view.findViewById(R.id.pinned_ssl_certificate_switch);
+        SwitchCompat pinnedSslCertificateSwitch = view.findViewById(R.id.pinned_ssl_certificate_switch);
         RadioButton currentWebsiteCertificateRadioButton = view.findViewById(R.id.current_website_certificate_radiobutton);
-        Switch pinnedIpAddressesSwitch = view.findViewById(R.id.pinned_ip_addresses_switch);
+        SwitchCompat pinnedIpAddressesSwitch = view.findViewById(R.id.pinned_ip_addresses_switch);
         RadioButton currentIpAddressesRadioButton = view.findViewById(R.id.current_ip_addresses_radiobutton);
 
         // Extract the data for the domain settings.
         String domainNameString = domainNameEditText.getText().toString();
         boolean javaScript = javaScriptSwitch.isChecked();
-        boolean firstPartyCookies = firstPartyCookiesSwitch.isChecked();
-        boolean thirdPartyCookies = thirdPartyCookiesSwitch.isChecked();
+        boolean cookies = cookiesSwitch.isChecked();
         boolean domStorage  = domStorageSwitch.isChecked();
         boolean formData = formDataSwitch.isChecked();  // Form data can be removed once the minimum API >= 26.
         boolean easyList = easyListSwitch.isChecked();
@@ -716,10 +775,10 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo
         boolean ultraList = ultraListSwitch.isChecked();
         boolean ultraPrivacy = ultraPrivacySwitch.isChecked();
         boolean blockAllThirdPartyRequests = blockAllThirdPartyRequestsSwitch.isChecked();
-        int userAgentPosition = userAgentSpinner.getSelectedItemPosition();
-        int fontSizePosition = fontSizeSpinner.getSelectedItemPosition();
+        int userAgentSwitchPosition = userAgentSpinner.getSelectedItemPosition();
+        int fontSizeSwitchPosition = fontSizeSpinner.getSelectedItemPosition();
         int swipeToRefreshInt = swipeToRefreshSpinner.getSelectedItemPosition();
-        int nightModeInt = nightModeSpinner.getSelectedItemPosition();
+        int webViewThemeInt = webViewThemeSpinner.getSelectedItemPosition();
         int wideViewportInt = wideViewportSpinner.getSelectedItemPosition();
         int displayWebpageImagesInt = displayWebpageImagesSpinner.getSelectedItemPosition();
         boolean pinnedSslCertificate = pinnedSslCertificateSwitch.isChecked();
@@ -729,7 +788,7 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo
         String userAgentName;
 
         // Set the user agent name.
-        switch (userAgentPosition) {
+        switch (userAgentSwitchPosition) {
             case MainWebViewActivity.DOMAINS_SYSTEM_DEFAULT_USER_AGENT:
                 // Set the user agent name to be `System default user agent`.
                 userAgentName = resources.getString(R.string.system_default_user_agent);
@@ -745,15 +804,21 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo
                 String[] userAgentNameArray = resources.getStringArray(R.array.user_agent_names);
 
                 // Set the user agent name from the array.  The domain spinner has one more entry than the name array, so the position must be decremented.
-                userAgentName = userAgentNameArray[userAgentPosition - 1];
+                userAgentName = userAgentNameArray[userAgentSwitchPosition - 1];
         }
 
-        // Get the font size integer.
-        int fontSizeInt = Integer.parseInt(resources.getStringArray(R.array.domain_settings_font_size_entry_values)[fontSizePosition]);
+        // Initialize the font size integer.  `0` indicates the system default font size.
+        int fontSizeInt = 0;
+
+        // Use a custom font size if it is selected.
+        if (fontSizeSwitchPosition == 1) {  // A custom font size is specified.
+            // Get the custom font size from the edit text.
+            fontSizeInt = Integer.parseInt(customFontSizeEditText.getText().toString());
+        }
 
         // Save the domain settings.
-        domainsDatabaseHelper.updateDomain(DomainsActivity.currentDomainDatabaseId, domainNameString, javaScript, firstPartyCookies, thirdPartyCookies, domStorage, formData, easyList, easyPrivacy,
-                fanboysAnnoyance, fanboysSocialBlocking, ultraList, ultraPrivacy, blockAllThirdPartyRequests, userAgentName, fontSizeInt, swipeToRefreshInt, nightModeInt, wideViewportInt,
+        domainsDatabaseHelper.updateDomain(DomainsActivity.currentDomainDatabaseId, domainNameString, javaScript, cookies, domStorage, formData, easyList, easyPrivacy,
+                fanboysAnnoyance, fanboysSocialBlocking, ultraList, ultraPrivacy, blockAllThirdPartyRequests, userAgentName, fontSizeInt, swipeToRefreshInt, webViewThemeInt, wideViewportInt,
                 displayWebpageImagesInt, pinnedSslCertificate, pinnedIpAddress);
 
         // Update the pinned SSL certificate if a new one is checked.
@@ -770,7 +835,7 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo
         }
     }
 
-    private void populateDomainsListView(final int highlightedDomainDatabaseId) {
+    private void populateDomainsListView(final int highlightedDomainDatabaseId, int domainsListViewPosition) {
         // get a handle for the current `domains_listview`.
         domainsListView = findViewById(R.id.domains_listview);
 
@@ -788,7 +853,7 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo
             @Override
             public void bindView(View view, Context context, Cursor cursor) {
                 // Set the domain name.
-                String domainNameString = cursor.getString(cursor.getColumnIndex(DomainsDatabaseHelper.DOMAIN_NAME));
+                String domainNameString = cursor.getString(cursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DOMAIN_NAME));
                 TextView domainNameTextView = view.findViewById(R.id.domain_name_textview);
                 domainNameTextView.setText(domainNameString);
             }
@@ -797,6 +862,9 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo
         // Update the list view.
         domainsListView.setAdapter(domainsCursorAdapter);
 
+        // Restore the scroll position.
+        domainsListView.setSelection(domainsListViewPosition);
+
         // Display the domain settings in the second pane if operating in two pane mode and the database contains at least one domain.
         if (DomainsActivity.twoPanedMode && (domainsCursor.getCount() > 0)) {  // Two-paned mode is enabled and there is at least one domain.
             // Initialize `highlightedDomainPosition`.
@@ -808,7 +876,7 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo
                 domainsCursor.moveToPosition(i);
 
                 // Get the database ID for this position.
-                int currentDatabaseId = domainsCursor.getInt(domainsCursor.getColumnIndex(DomainsDatabaseHelper._ID));
+                int currentDatabaseId = domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper._ID));
 
                 // Set `highlightedDomainPosition` if the database ID for this matches `highlightedDomainDatabaseId`.
                 if (highlightedDomainDatabaseId == currentDatabaseId) {
@@ -821,14 +889,19 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo
 
             // Get the database ID for the highlighted domain.
             domainsCursor.moveToPosition(highlightedDomainPosition);
-            currentDomainDatabaseId = domainsCursor.getInt(domainsCursor.getColumnIndex(DomainsDatabaseHelper._ID));
+            currentDomainDatabaseId = domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper._ID));
 
-            // Store the database ID in the arguments bundle.
+            // Create an arguments bundle.
             Bundle argumentsBundle = new Bundle();
+
+            // Store the domain settings in the arguments bundle.
             argumentsBundle.putInt(DomainSettingsFragment.DATABASE_ID, currentDomainDatabaseId);
+            argumentsBundle.putInt(DomainSettingsFragment.SCROLL_Y, domainSettingsScrollY);
 
-            // Add and arguments bundle to the domain settings fragment.
+            // Instantiate a new domain settings fragment.
             DomainSettingsFragment domainSettingsFragment = new DomainSettingsFragment();
+
+            // Add the arguments bundle to the domain settings fragment.
             domainSettingsFragment.setArguments(argumentsBundle);
 
             // Display the domain settings fragment.
@@ -837,22 +910,12 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo
             // Enable the delete options menu items.
             deleteMenuItem.setEnabled(true);
 
-            // Get a handle for the shared preferences.
-            SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
-
-            // Get the theme and screenshot preferences.
-            boolean darkTheme = sharedPreferences.getBoolean("dark_theme", false);
-
-            // Set the delete icon according to the theme.
-            if (darkTheme) {
-                deleteMenuItem.setIcon(R.drawable.delete_dark);
-            } else {
-                deleteMenuItem.setIcon(R.drawable.delete_light);
-            }
+            // Set the delete icon.
+            deleteMenuItem.setIcon(R.drawable.delete_enabled);
         } else if (twoPanedMode) {  // Two-paned mode is enabled but there are no domains.
             // Disable the options `MenuItems`.
             deleteMenuItem.setEnabled(false);
-            deleteMenuItem.setIcon(R.drawable.delete_blue);
+            deleteMenuItem.setIcon(R.drawable.delete_disabled);
         }
     }