]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/commitdiff
Fix the WebView layout with bottom app bars. https://redmine.stoutner.com/issues/839
authorSoren Stoutner <soren@stoutner.com>
Sat, 9 Apr 2022 22:03:51 +0000 (15:03 -0700)
committerSoren Stoutner <soren@stoutner.com>
Sat, 9 Apr 2022 22:03:51 +0000 (15:03 -0700)
17 files changed:
app/build.gradle
app/src/main/java/com/stoutner/privacybrowser/activities/ImportExportActivity.java
app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java
app/src/main/java/com/stoutner/privacybrowser/fragments/AboutWebViewFragment.kt
app/src/main/java/com/stoutner/privacybrowser/fragments/SettingsFragment.java
app/src/main/java/com/stoutner/privacybrowser/helpers/BookmarksDatabaseHelper.kt
app/src/main/java/com/stoutner/privacybrowser/helpers/DomainsDatabaseHelper.kt
app/src/main/java/com/stoutner/privacybrowser/helpers/ImportExportDatabaseHelper.java [deleted file]
app/src/main/java/com/stoutner/privacybrowser/helpers/ImportExportDatabaseHelper.kt [new file with mode: 0644]
app/src/main/java/com/stoutner/privacybrowser/viewmodelfactories/WebViewSourceFactory.kt
app/src/main/java/com/stoutner/privacybrowser/views/NestedScrollWebView.kt
app/src/main/res/values-de/strings.xml
app/src/main/res/values-es/strings.xml
app/src/main/res/values-it/strings.xml
app/src/main/res/values-ru/strings.xml
app/src/main/res/values/strings.xml
build.gradle

index 8469c24dc5e69944e23c25c325da244280039713..954739380799fadcfb1ec467cfea4f9bde53f2c0 100644 (file)
@@ -79,7 +79,7 @@ dependencies {
     implementation 'androidx.webkit:webkit:1.4.0'
 
     // Include the Kotlin standard library.  This should be the same version number listed in project build.gradle.
-    implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.10'
+    implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.20'
 
     // Include the Google material library.
     implementation 'com.google.android.material:material:1.5.0'
index fe4f99a02044be07b1dce82cd19fe6a9763b1778..548316dddba222bb8184f982902a2088ee3e7145 100644 (file)
@@ -531,6 +531,7 @@ public class ImportExportActivity extends AppCompatActivity {
                 case NO_ENCRYPTION:
                     try {
                         // Get an input stream for the file name.
+                        // A file may be opened directly once the minimum API >= 29.  <https://developer.android.com/reference/kotlin/android/content/ContentResolver#openfile>
                         InputStream inputStream = getContentResolver().openInputStream(Uri.parse(fileNameString));
 
                         // Import the unencrypted file.
@@ -724,6 +725,7 @@ public class ImportExportActivity extends AppCompatActivity {
 
                     try {
                         // Get the export file output stream.
+                        // A file may be opened directly once the minimum API >= 29.  <https://developer.android.com/reference/kotlin/android/content/ContentResolver#openfile>
                         OutputStream exportFileOutputStream = getContentResolver().openOutputStream(Uri.parse(noEncryptionFileNameString));
 
                         // Export the unencrypted file.
index 45a1de5b1c94e2c9267c6355eeaa8e9ccf113622..8344d5c4e168a4f15adfe9a581a92edc0ed9fcd3 100644 (file)
@@ -535,12 +535,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Set the theme.
         setTheme(R.style.PrivacyBrowser);
 
-        // Set the content view.
-        if (bottomAppBar) {
-            setContentView(R.layout.main_framelayout_bottom_appbar);
-        } else {
-            setContentView(R.layout.main_framelayout_top_appbar);
-        }
+        // 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);
@@ -3553,8 +3550,26 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Apply the proxy.
         applyProxy(false);
 
-        // Adjust the layout and scrolling parameters if the app bar is at the top of the screen.
-        if (!bottomAppBar) {
+        // Adjust the layout and scrolling parameters according to the position of the app bar.
+        if (bottomAppBar) {  // The app bar is on the bottom.
+            // Adjust the UI.
+            if (scrollAppBar || (inFullScreenBrowsingMode && hideAppBar)) {  // The app bar scrolls or full screen browsing mode is engaged with the app bar hidden.
+                // Reset the WebView padding to fill the available space.
+                swipeRefreshLayout.setPadding(0, 0, 0, 0);
+            } else {  // The app bar doesn't scroll or full screen browsing mode is not engaged with the app bar hidden.
+                // Move the WebView above the app bar layout.
+                swipeRefreshLayout.setPadding(0, 0, 0, appBarHeight);
+
+                // Show the app bar if it is scrolled off the screen.
+                if (appBarLayout.getTranslationY() != 0) {
+                    // Animate the bottom app bar onto the screen.
+                    objectAnimator = ObjectAnimator.ofFloat(appBarLayout, "translationY", 0);
+
+                    // Make it so.
+                    objectAnimator.start();
+                }
+            }
+        } else {  // The app bar is on the top.
             // Get the current layout parameters.  Using coordinator layout parameters allows the `setBehavior()` command and using app bar layout parameters allows the `setScrollFlags()` command.
             CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams();
             AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) toolbar.getLayoutParams();
@@ -5121,7 +5136,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     // Toggle the full screen browsing mode.
                     if (inFullScreenBrowsingMode) {  // Switch to full screen mode.
                         // Hide the app bar if specified.
-                        if (hideAppBar) {
+                        if (hideAppBar) {  // The app bar is hidden.
                             // Close the find on page bar if it is visible.
                             closeFindOnPage(null);
 
@@ -5131,8 +5146,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                             // Hide the action bar.
                             actionBar.hide();
 
-                            // Set layout and scrolling parameters if the app bar is at the top of the screen.
-                            if (!bottomAppBar) {
+                            // Set layout and scrolling parameters according to the position of the app bar.
+                            if (bottomAppBar) {  // The app bar is at the bottom.
+                                // Reset the WebView padding to fill the available space.
+                                swipeRefreshLayout.setPadding(0, 0, 0, 0);
+                            } else {  // The app bar is at the top.
                                 // Check to see if the app bar is normally scrolled.
                                 if (scrollAppBar) {  // The app bar is scrolled when it is displayed.
                                     // Get the swipe refresh layout parameters.
@@ -5148,6 +5166,18 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                                     swipeRefreshLayout.setProgressViewOffset(false, -200, defaultProgressViewEndOffset);
                                 }
                             }
+                        } else {  // The app bar is not hidden.
+                            // Adjust the UI for the bottom app bar.
+                            if (bottomAppBar) {
+                                // Adjust the UI according to the scrolling of the app bar.
+                                if (scrollAppBar) {
+                                    // Reset the WebView padding to fill the available space.
+                                    swipeRefreshLayout.setPadding(0, 0, 0, 0);
+                                } else {
+                                    // Move the WebView above the app bar layout.
+                                    swipeRefreshLayout.setPadding(0, 0, 0, appBarHeight);
+                                }
+                            }
                         }
 
                         /* Hide the system bars.
@@ -5166,23 +5196,32 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                             // Show the action bar.
                             actionBar.show();
+                        }
 
-                            // Set layout and scrolling parameters if the app bar is at the top of the screen.
-                            if (!bottomAppBar) {
-                                // Check to see if the app bar is normally scrolled.
-                                if (scrollAppBar) {  // The app bar is scrolled when it is displayed.
-                                    // Get the swipe refresh layout parameters.
-                                    CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams();
-
-                                    // Add the off-screen scrolling layout.
-                                    swipeRefreshLayoutParams.setBehavior(new AppBarLayout.ScrollingViewBehavior());
-                                } else {  // The app bar is not scrolled when it is displayed.
-                                    // The swipe refresh layout must be manually moved below the app bar layout.
-                                    swipeRefreshLayout.setPadding(0, appBarHeight, 0, 0);
-
-                                    // The swipe to refresh circle doesn't always hide itself completely unless it is moved up 10 pixels.
-                                    swipeRefreshLayout.setProgressViewOffset(false, defaultProgressViewStartOffset - 10 + appBarHeight, defaultProgressViewEndOffset + appBarHeight);
-                                }
+                        // Set layout and scrolling parameters according to the position of the app bar.
+                        if (bottomAppBar) {  // The app bar is at the bottom.
+                            // Adjust the UI.
+                            if (scrollAppBar) {
+                                // Reset the WebView padding to fill the available space.
+                                swipeRefreshLayout.setPadding(0, 0, 0, 0);
+                            } else {
+                                // Move the WebView above the app bar layout.
+                                swipeRefreshLayout.setPadding(0, 0, 0, appBarHeight);
+                            }
+                        } else {  // The app bar is at the top.
+                            // Check to see if the app bar is normally scrolled.
+                            if (scrollAppBar) {  // The app bar is scrolled when it is displayed.
+                                // Get the swipe refresh layout parameters.
+                                CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams();
+
+                                // Add the off-screen scrolling layout.
+                                swipeRefreshLayoutParams.setBehavior(new AppBarLayout.ScrollingViewBehavior());
+                            } else {  // The app bar is not scrolled when it is displayed.
+                                // The swipe refresh layout must be manually moved below the app bar layout.
+                                swipeRefreshLayout.setPadding(0, appBarHeight, 0, 0);
+
+                                // The swipe to refresh circle doesn't always hide itself completely unless it is moved up 10 pixels.
+                                swipeRefreshLayout.setProgressViewOffset(false, defaultProgressViewStartOffset - 10 + appBarHeight, defaultProgressViewEndOffset + appBarHeight);
                             }
                         }
 
@@ -5196,6 +5235,30 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     return false;
                 }
             }
+
+            @Override
+            public boolean onFling(MotionEvent motionEvent1, MotionEvent motionEvent2, float velocityX, float velocityY) {
+                // Scroll the bottom app bar if enabled.
+                if (bottomAppBar && scrollAppBar && !objectAnimator.isRunning()) {
+                    // Calculate the Y change.
+                    float motionY = motionEvent2.getY() - motionEvent1.getY();
+
+                    // Scroll the app bar if the change is greater than 100 pixels.
+                    if (motionY > 50) {
+                        // Animate the bottom app bar onto the screen.
+                        objectAnimator = ObjectAnimator.ofFloat(appBarLayout, "translationY", 0);
+                    } else if (motionY < -50) {
+                        // Animate the bottom app bar off the screen.
+                        objectAnimator = ObjectAnimator.ofFloat(appBarLayout, "translationY", appBarLayout.getHeight());
+                    }
+
+                    // Make it so.
+                    objectAnimator.start();
+                }
+
+                // Do not consume the event.
+                return false;
+            }
         });
 
         // Pass all touch events on the WebView through the double-tap gesture detector.
@@ -5269,7 +5332,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             }
         });
 
-        // Update the status of swipe to refresh based on the scroll position of the nested scroll WebView.  Also reinforce full screen browsing mode.
+        // Process scroll changes.
         nestedScrollWebView.setOnScrollChangeListener((view, scrollX, scrollY, oldScrollX, oldScrollY) -> {
             // Set the swipe to refresh status.
             if (nestedScrollWebView.getSwipeToRefresh()) {
@@ -5280,23 +5343,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 swipeRefreshLayout.setEnabled(false);
             }
 
-            //  Scroll the bottom app bar if enabled.
-            if (bottomAppBar && scrollAppBar && !objectAnimator.isRunning()) {
-                if (scrollY < oldScrollY) {  // The WebView was scrolled down.
-                    // Animate the bottom app bar onto the screen.
-                    objectAnimator = ObjectAnimator.ofFloat(appBarLayout, "translationY", 0);
-
-                    // Make it so.
-                    objectAnimator.start();
-                } else if (scrollY > oldScrollY) {  // The WebView was scrolled up.
-                    // Animate the bottom app bar off the screen.
-                    objectAnimator = ObjectAnimator.ofFloat(appBarLayout, "translationY", appBarLayout.getHeight());
-
-                    // Make it so.
-                    objectAnimator.start();
-                }
-            }
-
             // Reinforce the system UI visibility flags if in full screen browsing mode.
             // This hides the status and navigation bars, which are displayed if other elements are shown, like dialog boxes, the options menu, or the keyboard.
             if (inFullScreenBrowsingMode) {
@@ -5909,8 +5955,21 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
             @Override
             public void onPageStarted(WebView view, String url, Bitmap favicon) {
-                // Set the padding and layout settings if the app bar is at the top of the screen.
-                if (!bottomAppBar) {
+                // Get the app bar layout height.  This can't be done in `applyAppSettings()` because the app bar is not yet populated there.
+                // This should only be populated if it is greater than 0 because otherwise it will be reset to 0 if the app bar is hidden in full screen browsing mode.
+                if (appBarLayout.getHeight() > 0) appBarHeight = appBarLayout.getHeight();
+
+                // Set the padding and layout settings according to the position of the app bar.
+                if (bottomAppBar) {  // The app bar is on the bottom.
+                    // Adjust the UI.
+                    if (scrollAppBar || (inFullScreenBrowsingMode && hideAppBar)) {  // The app bar scrolls or full screen browsing mode is engaged with the app bar hidden.
+                        // Reset the WebView padding to fill the available space.
+                        swipeRefreshLayout.setPadding(0, 0, 0, 0);
+                    } else {  // The app bar doesn't scroll or full screen browsing mode is not engaged with the app bar hidden.
+                        // Move the WebView above the app bar layout.
+                        swipeRefreshLayout.setPadding(0, 0, 0, appBarHeight);
+                    }
+                } else {  // The app bar is on the top.
                     // Set the top padding of the swipe refresh layout according to the app bar scrolling preference.  This can't be done in `appAppSettings()` because the app bar is not yet populated there.
                     if (scrollAppBar || (inFullScreenBrowsingMode && hideAppBar)) {
                         // No padding is needed because it will automatically be placed below the app bar layout due to the scrolling layout behavior.
@@ -5919,9 +5978,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                         // The swipe to refresh circle doesn't always hide itself completely unless it is moved up 10 pixels.
                         swipeRefreshLayout.setProgressViewOffset(false, defaultProgressViewStartOffset - 10, defaultProgressViewEndOffset);
                     } else {
-                        // Get the app bar layout height.  This can't be done in `applyAppSettings()` because the app bar is not yet populated there.
-                        appBarHeight = appBarLayout.getHeight();
-
                         // The swipe refresh layout must be manually moved below the app bar layout.
                         swipeRefreshLayout.setPadding(0, appBarHeight, 0, 0);
 
index 3f14e183bd8f00cf273e19b3c3e7b34e9b7cbf3f..3b516ec546d53b5824f51f3ffdc4a1348999f611 100644 (file)
@@ -91,6 +91,7 @@ class AboutWebViewFragment : Fragment() {
         // Set a WebView client.
         tabWebView.webViewClient = object : WebViewClient() {
             // // Send external links back to the main Privacy Browser WebView.  The deprecated `shouldOverrideUrlLoading` must be used until API >= 24.
+            @Deprecated("Deprecated in Java")
             override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
                 // Create an intent to view the URL.
                 val urlIntent = Intent(Intent.ACTION_VIEW)
index 05b82dff6e8a2bddfb3b668a4f007acb0534cc95..e17f82728b6968eb9a4f4ba063ebfb31ed2ea5aa 100644 (file)
@@ -1230,20 +1230,15 @@ public class SettingsFragment extends PreferenceFragmentCompat {
 
                 case "scroll_app_bar":
                     // Update the icon.
-                    if (sharedPreferences.getBoolean(context.getString(R.string.scroll_app_bar_key), true)) {
-                        scrollAppBarPreference.setIcon(R.drawable.app_bar_enabled);
-                    } else {
-                        scrollAppBarPreference.setIcon(R.drawable.app_bar_disabled);
-                    }
+                    if (sharedPreferences.getBoolean(context.getString(R.string.scroll_app_bar_key), true)) scrollAppBarPreference.setIcon(R.drawable.app_bar_enabled);
+                    else scrollAppBarPreference.setIcon(R.drawable.app_bar_disabled);
+
                     break;
 
                 case "bottom_app_bar":
                     // Update the icon.
-                    if (sharedPreferences.getBoolean(context.getString(R.string.bottom_app_bar_key), false)) {
-                        bottomAppBarPreference.setIcon(R.drawable.bottom_app_bar_enabled);
-                    } else {
-                        bottomAppBarPreference.setIcon(R.drawable.bottom_app_bar_disabled);
-                    }
+                    if (sharedPreferences.getBoolean(context.getString(R.string.bottom_app_bar_key), false)) bottomAppBarPreference.setIcon(R.drawable.bottom_app_bar_enabled);
+                    else bottomAppBarPreference.setIcon(R.drawable.bottom_app_bar_disabled);
 
                     // Restart Privacy Browser.
                     restartPrivacyBrowser();
index 12753e1652d5947eb2f0e2d07719dee11f136d76..135827ec64e48b835dc929cbf91a8d498a170904 100644 (file)
@@ -26,17 +26,17 @@ import android.database.DatabaseUtils
 import android.database.sqlite.SQLiteDatabase
 import android.database.sqlite.SQLiteOpenHelper
 
-// The private constants.
+// Define the private class constants.
 private const val SCHEMA_VERSION = 1
 
 class BookmarksDatabaseHelper(context: Context) : SQLiteOpenHelper(context, BOOKMARKS_DATABASE, null, SCHEMA_VERSION) {
     // Define the public companion object constants.  These can be moved to public class constants once the entire project has migrated to Kotlin.
     companion object {
-        // The database constants.
+        // Define the public database constants.
         const val BOOKMARKS_DATABASE = "bookmarks.db"
         const val BOOKMARKS_TABLE = "bookmarks"
 
-        // The schema constants.
+        // Define the public schema constants.
         const val ID = "_id"
         const val BOOKMARK_NAME = "bookmarkname"
         const val BOOKMARK_URL = "bookmarkurl"
@@ -45,7 +45,7 @@ class BookmarksDatabaseHelper(context: Context) : SQLiteOpenHelper(context, BOOK
         const val IS_FOLDER = "isfolder"
         const val FAVORITE_ICON = "favoriteicon"
 
-        // The table creation constant.
+        // Define the public table creation constant.
         const val CREATE_BOOKMARKS_TABLE = "CREATE TABLE " + BOOKMARKS_TABLE + " (" +
                 ID + " INTEGER PRIMARY KEY, " +
                 BOOKMARK_NAME + " TEXT, " +
index 4fc6a0813e332bdb7a996d935676be23f627effe..53a3cbe4569c84e22202de8494ccef8b7fd06051 100644 (file)
@@ -119,141 +119,141 @@ class DomainsDatabaseHelper(private val appContext: Context) : SQLiteOpenHelper(
     }
 
     override fun onUpgrade(domainsDatabase: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
-        // Upgrade the database table.
-        when (oldVersion) {
-            // Upgrade from schema version 1.  TODO.  Test to make sure updates work across multiple versions.
-            1 -> {
-                // Add the display images column.
-                domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $DISPLAY_IMAGES INTEGER")
-            }
+        // Upgrade from schema version 1, first used in Privacy Browser 2.0, to schema version 2, first used in Privacy Browser 2.3.
+        if (oldVersion < 2) {
+            // Add the display images column.
+            domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $DISPLAY_IMAGES INTEGER")
+        }
 
-            // Upgrade from schema version 2.
-            2 -> {
-                //  Add the SSL certificate columns.
-                domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $PINNED_SSL_CERTIFICATE BOOLEAN")
-                domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $SSL_ISSUED_TO_COMMON_NAME TEXT")
-                domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $SSL_ISSUED_TO_ORGANIZATION TEXT")
-                domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $SSL_ISSUED_TO_ORGANIZATIONAL_UNIT TEXT")
-                domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $SSL_ISSUED_BY_COMMON_NAME TEXT")
-                domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $SSL_ISSUED_BY_ORGANIZATION TEXT")
-                domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $SSL_ISSUED_BY_ORGANIZATIONAL_UNIT TEXT")
-                domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $SSL_START_DATE INTEGER")
-                domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $SSL_END_DATE INTEGER")
-            }
+        // Upgrade from schema version 2, first used in Privacy Browser 2.3, to schema version 3, first used in Privacy Browser 2.5.
+        if (oldVersion < 3) {
+            //  Add the SSL certificate columns.
+            domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $PINNED_SSL_CERTIFICATE BOOLEAN")
+            domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $SSL_ISSUED_TO_COMMON_NAME TEXT")
+            domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $SSL_ISSUED_TO_ORGANIZATION TEXT")
+            domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $SSL_ISSUED_TO_ORGANIZATIONAL_UNIT TEXT")
+            domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $SSL_ISSUED_BY_COMMON_NAME TEXT")
+            domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $SSL_ISSUED_BY_ORGANIZATION TEXT")
+            domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $SSL_ISSUED_BY_ORGANIZATIONAL_UNIT TEXT")
+            domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $SSL_START_DATE INTEGER")
+            domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $SSL_END_DATE INTEGER")
+        }
+
+        // Upgrade from schema version 3, first used in Privacy Browser 2.5, to schema version 4, first used in Privacy Browser 2.6.
+        if (oldVersion < 4) {
+            // Add the night mode column.
+            domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN nightmode INTEGER")
+        }
 
-            // Upgrade from schema version 3.
-            3 -> {
-                // Add the night mode column.
-                domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN nightmode INTEGER")
+        // Upgrade from schema version 4, first used in Privacy Browser 2.6, to schema version 5, first used in Privacy Browser 2.9.
+        if (oldVersion < 5) {
+            // Add the block lists columns.
+            domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $ENABLE_EASYLIST BOOLEAN")
+            domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $ENABLE_EASYPRIVACY BOOLEAN")
+            domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $ENABLE_FANBOYS_ANNOYANCE_LIST BOOLEAN")
+            domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST BOOLEAN")
+
+            // Get a handle for the shared preference.
+            val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(appContext)
+
+            // Get the default block list settings.
+            val easyListEnabled = sharedPreferences.getBoolean("easylist", true)
+            val easyPrivacyEnabled = sharedPreferences.getBoolean("easyprivacy", true)
+            val fanboyAnnoyanceListEnabled = sharedPreferences.getBoolean("fanboys_annoyance_list", true)
+            val fanboySocialBlockingListEnabled = sharedPreferences.getBoolean("fanboys_social_blocking_list", true)
+
+            // Set EasyList for existing rows according to the current system-wide default.
+            // This can switch to using the variables directly once the API >= 30.  <https://www.sqlite.org/datatype3.html#boolean_datatype>
+            // <https://developer.android.com/reference/android/database/sqlite/package-summary>
+            if (easyListEnabled) {
+                domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ENABLE_EASYLIST = 1")
+            } else {
+                domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ENABLE_EASYLIST = 0")
             }
 
-            // Upgrade from schema version 4.
-            4 -> {
-                // Add the block lists columns.
-                domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $ENABLE_EASYLIST BOOLEAN")
-                domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $ENABLE_EASYPRIVACY BOOLEAN")
-                domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $ENABLE_FANBOYS_ANNOYANCE_LIST BOOLEAN")
-                domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST BOOLEAN")
-
-                // Get a handle for the shared preference.
-                val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(appContext)
-
-                // Get the default block list settings.
-                val easyListEnabled = sharedPreferences.getBoolean("easylist", true)
-                val easyPrivacyEnabled = sharedPreferences.getBoolean("easyprivacy", true)
-                val fanboyAnnoyanceListEnabled = sharedPreferences.getBoolean("fanboys_annoyance_list", true)
-                val fanboySocialBlockingListEnabled = sharedPreferences.getBoolean("fanboys_social_blocking_list", true)
-
-                // Set EasyList for existing rows according to the current system-wide default.
-                if (easyListEnabled) {
-                    domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ENABLE_EASYLIST = 1")
-                } else {
-                    domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ENABLE_EASYLIST = 0")
-                }
-
-                // Set EasyPrivacy for existing rows according to the current system-wide default.
-                if (easyPrivacyEnabled) {
-                    domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ENABLE_EASYPRIVACY = 1")
-                } else {
-                    domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ENABLE_EASYPRIVACY = 0")
-                }
-
-                // Set Fanboy's Annoyance List for existing rows according to the current system-wide default.
-                if (fanboyAnnoyanceListEnabled) {
-                    domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ENABLE_FANBOYS_ANNOYANCE_LIST = 1")
-                } else {
-                    domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ENABLE_FANBOYS_ANNOYANCE_LIST = 0")
-                }
-
-                // Set Fanboy's Social Blocking List for existing rows according to the current system-wide default.
-                if (fanboySocialBlockingListEnabled) {
-                    domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST = 1")
-                } else {
-                    domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST = 0")
-                }
+            // Set EasyPrivacy for existing rows according to the current system-wide default.
+            if (easyPrivacyEnabled) {
+                domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ENABLE_EASYPRIVACY = 1")
+            } else {
+                domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ENABLE_EASYPRIVACY = 0")
             }
 
-            // Upgrade from schema version 5.
-            5 -> {
-                // Add the swipe to refresh column.  This defaults to `0`, which is `System default`, so a separate step isn't needed to populate the column.
-                domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $SWIPE_TO_REFRESH INTEGER")
+            // Set Fanboy's Annoyance List for existing rows according to the current system-wide default.
+            if (fanboyAnnoyanceListEnabled) {
+                domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ENABLE_FANBOYS_ANNOYANCE_LIST = 1")
+            } else {
+                domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ENABLE_FANBOYS_ANNOYANCE_LIST = 0")
             }
 
-            // Upgrade from schema version 6.
-            6 -> {
-                // Add the block all third-party requests column.  This defaults to `0`, which is off, so a separate step isn't needed to populate the column.
-                domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $BLOCK_ALL_THIRD_PARTY_REQUESTS BOOLEAN")
+            // Set Fanboy's Social Blocking List for existing rows according to the current system-wide default.
+            if (fanboySocialBlockingListEnabled) {
+                domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST = 1")
+            } else {
+                domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST = 0")
             }
+        }
 
-            // Upgrade from schema version 7.
-            7 -> {
-                // Add the UltraPrivacy column.
-                domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $ENABLE_ULTRAPRIVACY BOOLEAN")
+        // Upgrade from schema version 5, first used in Privacy Browser 2.9, to schema version 6, first used in Privacy Browser 2.11.
+        if (oldVersion < 6) {
+            // Add the swipe to refresh column.  This defaults to `0`, which is `System default`, so a separate step isn't needed to populate the column.
+            domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $SWIPE_TO_REFRESH INTEGER")
+        }
 
-                // Enable it for all existing rows.
-                domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ENABLE_ULTRAPRIVACY = 1")
-            }
+        // Upgrade from schema version 6, first used in Privacy Browser 2.11, to schema version 7, first used in Privacy Browser 2.12.
+        if (oldVersion < 7) {
+            // Add the block all third-party requests column.  This defaults to `0`, which is off, so a separate step isn't needed to populate the column.
+            domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $BLOCK_ALL_THIRD_PARTY_REQUESTS BOOLEAN")
+        }
 
-            // Upgrade from schema version 8.
-            8 -> {
-                // Add the pinned IP addresses columns.  These default to `0` and `""`, so a separate step isn't needed to populate the columns.
-                domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $PINNED_IP_ADDRESSES BOOLEAN")
-                domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $IP_ADDRESSES TEXT")
-            }
+        // Upgrade from schema version 7, first used in Privacy Browser 2.12, to schema version 8, first used in Privacy Browser 2.12.
+        // For some reason (lack of planning or attention to detail), the 2.12 update included two schema version jumps.
+        if (oldVersion < 8) {
+            // Add the UltraPrivacy column.
+            domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $ENABLE_ULTRAPRIVACY BOOLEAN")
 
-            // Upgrade from schema version 9.
-            9 -> {
-                // Add the wide viewport column.  This defaults to `0`, which is `System default`, so a separate step isn't needed to populate the column.
-                domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $WIDE_VIEWPORT INTEGER")
-            }
+            // Enable it for all existing rows.
+            domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ENABLE_ULTRAPRIVACY = 1")
+        }
 
-            // Upgrade from schema version 10.
-            10 -> {
-                // Add the UltraList column.
-                domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $ULTRALIST BOOLEAN")
+        // Upgrade from schema version 8, first used in Privacy Browser 2.12, to schema version 9, first used in Privacy Browser 2.16.
+        if (oldVersion < 9) {
+            // Add the pinned IP addresses columns.  These default to `0` and `""`, so a separate step isn't needed to populate the columns.
+            domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $PINNED_IP_ADDRESSES BOOLEAN")
+            domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $IP_ADDRESSES TEXT")
+        }
 
-                // Enable it for all existing rows.
-                domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ULTRALIST = 1")
-            }
+        // Upgrade from schema version 9, first used in Privacy Browser 2.16, to schema version 10, first used in Privacy Browser 3.1.
+        if (oldVersion < 10) {
+            // Add the wide viewport column.  This defaults to `0`, which is `System default`, so a separate step isn't needed to populate the column.
+            domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $WIDE_VIEWPORT INTEGER")
+        }
 
-            // Upgrade from schema version 11.
-            11 -> {
-                // Add the WebView theme column.  This defaults to `0`, which is `System default`, so a separate step isn't needed to populate the column.
-                domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $WEBVIEW_THEME INTEGER")
+        // Upgrade from schema version 10, first used in Privacy Browser 3.1, to schema version 11, first used in Privacy Browser 3.2.
+        if (oldVersion < 11) {
+            // Add the UltraList column.
+            domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $ULTRALIST BOOLEAN")
 
-                // SQLite amazingly only added a command to drop a column in version 3.35.0.  <https://www.sqlite.org/changes.html>
-                // It will be a while before that is supported in Android.  <https://developer.android.com/reference/android/database/sqlite/package-summary>
-                // Although a new table could be created and all the data copied to it, I think I will just leave the old night mode column.  It will be wiped out the next time an import is run.
-            }
+            // Enable it for all existing rows.
+            domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ULTRALIST = 1")
+        }
 
-            // Upgrade from schema version 12.
-            12 -> {
-                // Add the cookies column.
-                domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $COOKIES BOOLEAN")
+        // Upgrade from schema version 11, first used in Privacy Browser 3.2, to schema version 12, first used in Privacy Browser 3.5.
+        if (oldVersion < 12) {
+            // Add the WebView theme column.  This defaults to `0`, which is `System default`, so a separate step isn't needed to populate the column.
+            domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $WEBVIEW_THEME INTEGER")
 
-                // Copy the data from the old column to the new one.
-                domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $COOKIES = enablefirstpartycookies")
-            }
+            // SQLite amazingly only added a command to drop a column in version 3.35.0.  <https://www.sqlite.org/changes.html>
+            // It will be a while before that is supported in Android.  <https://developer.android.com/reference/android/database/sqlite/package-summary>
+            // Although a new table could be created and all the data copied to it, I think I will just leave the old night mode column.  It will be wiped out the next time an import is run.
+        }
+
+        // Upgrade from schema version 12, first used in Privacy Browser 3.5, to schema version 13, first used in Privacy Browser 3.8.
+        if (oldVersion < 13) {
+            // Add the cookies column.
+            domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $COOKIES BOOLEAN")
+
+            // Copy the data from the old column to the new one.
+            domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $COOKIES = enablefirstpartycookies")
         }
     }
 
diff --git a/app/src/main/java/com/stoutner/privacybrowser/helpers/ImportExportDatabaseHelper.java b/app/src/main/java/com/stoutner/privacybrowser/helpers/ImportExportDatabaseHelper.java
deleted file mode 100644 (file)
index 096364f..0000000
+++ /dev/null
@@ -1,794 +0,0 @@
-/*
- * Copyright © 2018-2022 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.helpers;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.database.sqlite.SQLiteDatabase;
-import android.preference.PreferenceManager;
-
-import com.stoutner.privacybrowser.R;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-public class ImportExportDatabaseHelper {
-    // Declare the public constants.
-    public static final String EXPORT_SUCCESSFUL = "Export Successful";
-    public static final String IMPORT_SUCCESSFUL = "Import Successful";
-
-    // Declare the class constants.
-    private static final int SCHEMA_VERSION = 14;
-    private static final String PREFERENCES_TABLE = "preferences";
-
-    // Declare the preferences constants.
-    private static final String _ID = "_id";
-    private static final String JAVASCRIPT = "javascript";
-    private static final String COOKIES = "cookies";
-    private static final String DOM_STORAGE = "dom_storage";
-    private static final String SAVE_FORM_DATA = "save_form_data";
-    private static final String USER_AGENT = "user_agent";
-    private static final String CUSTOM_USER_AGENT = "custom_user_agent";
-    private static final String INCOGNITO_MODE = "incognito_mode";
-    private static final String ALLOW_SCREENSHOTS = "allow_screenshots";
-    private static final String EASYLIST = "easylist";
-    private static final String EASYPRIVACY = "easyprivacy";
-    private static final String FANBOYS_ANNOYANCE_LIST = "fanboys_annoyance_list";
-    private static final String FANBOYS_SOCIAL_BLOCKING_LIST = "fanboys_social_blocking_list";
-    private static final String ULTRALIST = "ultralist";
-    private static final String ULTRAPRIVACY = "ultraprivacy";
-    private static final String BLOCK_ALL_THIRD_PARTY_REQUESTS = "block_all_third_party_requests";
-    private static final String GOOGLE_ANALYTICS = "google_analytics";
-    private static final String FACEBOOK_CLICK_IDS = "facebook_click_ids";
-    private static final String TWITTER_AMP_REDIRECTS = "twitter_amp_redirects";
-    private static final String SEARCH = "search";
-    private static final String SEARCH_CUSTOM_URL = "search_custom_url";
-    private static final String PROXY = "proxy";
-    private static final String PROXY_CUSTOM_URL = "proxy_custom_url";
-    private static final String FULL_SCREEN_BROWSING_MODE = "full_screen_browsing_mode";
-    private static final String HIDE_APP_BAR = "hide_app_bar";
-    private static final String CLEAR_EVERYTHING = "clear_everything";
-    private static final String CLEAR_COOKIES = "clear_cookies";
-    private static final String CLEAR_DOM_STORAGE = "clear_dom_storage";
-    private static final String CLEAR_FORM_DATA = "clear_form_data";
-    private static final String CLEAR_LOGCAT = "clear_logcat";
-    private static final String CLEAR_CACHE = "clear_cache";
-    private static final String HOMEPAGE = "homepage";
-    private static final String FONT_SIZE = "font_size";
-    private static final String OPEN_INTENTS_IN_NEW_TAB = "open_intents_in_new_tab";
-    private static final String SWIPE_TO_REFRESH = "swipe_to_refresh";
-    private static final String DOWNLOAD_WITH_EXTERNAL_APP = "download_with_external_app";
-    private static final String SCROLL_APP_BAR = "scroll_app_bar";
-    private static final String BOTTOM_APP_BAR = "bottom_app_bar";
-    private static final String DISPLAY_ADDITIONAL_APP_BAR_ICONS = "display_additional_app_bar_icons";
-    private static final String APP_THEME = "app_theme";
-    private static final String WEBVIEW_THEME = "webview_theme";
-    private static final String WIDE_VIEWPORT = "wide_viewport";
-    private static final String DISPLAY_WEBPAGE_IMAGES = "display_webpage_images";
-
-    public String importUnencrypted(InputStream importFileInputStream, Context context){
-        try {
-            // Create a temporary import file.
-            File temporaryImportFile = File.createTempFile("temporary_import_file", null, context.getCacheDir());
-
-            // Create a temporary file output stream.
-            FileOutputStream temporaryImportFileOutputStream = new FileOutputStream(temporaryImportFile);
-
-            // Create a transfer byte array.
-            byte[] transferByteArray = new byte[1024];
-
-            // Create an integer to track the number of bytes read.
-            int bytesRead;
-
-            // Copy the import file to the temporary import file.
-            while ((bytesRead = importFileInputStream.read(transferByteArray)) > 0) {
-                temporaryImportFileOutputStream.write(transferByteArray, 0, bytesRead);
-            }
-
-            // Flush the temporary import file output stream.
-            temporaryImportFileOutputStream.flush();
-
-            // Close the file streams.
-            importFileInputStream.close();
-            temporaryImportFileOutputStream.close();
-
-
-            // Get a handle for the shared preference.
-            SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
-
-            // Open the import database.  Once the minimum API >= 27 the file can be opened directly without using the string.
-            SQLiteDatabase importDatabase = SQLiteDatabase.openDatabase(temporaryImportFile.toString(), null, SQLiteDatabase.OPEN_READWRITE);
-
-            // Get the database version.
-            int importDatabaseVersion = importDatabase.getVersion();
-
-            // Upgrade the database if needed.
-            if (importDatabaseVersion < SCHEMA_VERSION) {
-                switch (importDatabaseVersion) {
-                    // Upgrade from schema version 1, Privacy Browser 2.13.
-                    case 1:
-                        // Previously this upgrade added `download_with_external_app` to the Preferences table.  But that is now removed in schema version 10.
-
-                    // Upgrade from schema version 2, Privacy Browser 2.14.
-                    case 2:
-                        // Once the SQLite version is >= 3.25.0 `ALTER TABLE RENAME COLUMN` can be used.  <https://www.sqlite.org/lang_altertable.html> <https://www.sqlite.org/changes.html>
-                        // <https://developer.android.com/reference/android/database/sqlite/package-summary>
-                        // In the meantime, a new column must be created with the new name.  There is no need to delete the old column on the temporary import database.
-
-                        // Create the new font size column.
-                        importDatabase.execSQL("ALTER TABLE " + PREFERENCES_TABLE + " ADD COLUMN " + FONT_SIZE + " TEXT");
-
-                        // Populate the preferences table with the current font size value.
-                        importDatabase.execSQL("UPDATE " + PREFERENCES_TABLE + " SET " + FONT_SIZE + " = default_font_size");
-
-                    // Upgrade from schema version 3, Privacy Browser 2.15.
-                    case 3:
-                        // Add the Pinned IP Addresses columns to the domains table.
-                        importDatabase.execSQL("ALTER TABLE " + DomainsDatabaseHelper.DOMAINS_TABLE + " ADD COLUMN " + DomainsDatabaseHelper.PINNED_IP_ADDRESSES + " BOOLEAN");
-                        importDatabase.execSQL("ALTER TABLE " + DomainsDatabaseHelper.DOMAINS_TABLE + " ADD COLUMN " + DomainsDatabaseHelper.IP_ADDRESSES + " TEXT");
-
-                    // Upgrade from schema version 4, Privacy Browser 2.16.
-                    case 4:
-                        // Add the hide and scroll app bar columns to the preferences table.
-                        importDatabase.execSQL("ALTER TABLE " + PREFERENCES_TABLE + " ADD COLUMN " + HIDE_APP_BAR + " BOOLEAN");
-                        importDatabase.execSQL("ALTER TABLE " + PREFERENCES_TABLE + " ADD COLUMN " + SCROLL_APP_BAR + " BOOLEAN");
-
-                        // Get the current hide and scroll app bar settings.
-                        boolean hideAppBar = sharedPreferences.getBoolean(HIDE_APP_BAR, true);
-                        boolean scrollAppBar = sharedPreferences.getBoolean(SCROLL_APP_BAR, true);
-
-                        // Populate the preferences table with the current app bar values.
-                        if (hideAppBar) {
-                            importDatabase.execSQL("UPDATE " + PREFERENCES_TABLE + " SET " + HIDE_APP_BAR + " = " + 1);
-                        } else {
-                            importDatabase.execSQL("UPDATE " + PREFERENCES_TABLE + " SET " + HIDE_APP_BAR + " = " + 0);
-                        }
-
-                        if (scrollAppBar) {
-                            importDatabase.execSQL("UPDATE " + PREFERENCES_TABLE + " SET " + SCROLL_APP_BAR + " = " + 1);
-                        } else {
-                            importDatabase.execSQL("UPDATE " + PREFERENCES_TABLE + " SET " + SCROLL_APP_BAR + " = " + 0);
-                        }
-
-                    // Upgrade from schema version 5, Privacy Browser 2.17.
-                    case 5:
-                        // Add the open intents in new tab column to the preferences table.
-                        importDatabase.execSQL("ALTER TABLE " + PREFERENCES_TABLE + " ADD COLUMN " + OPEN_INTENTS_IN_NEW_TAB + " BOOLEAN");
-
-                        // Get the current open intents in new tab preference.
-                        boolean openIntentsInNewTab = sharedPreferences.getBoolean(OPEN_INTENTS_IN_NEW_TAB, true);
-
-                        // Populate the preferences table with the current open intents value.
-                        if (openIntentsInNewTab) {
-                            importDatabase.execSQL("UPDATE " + PREFERENCES_TABLE + " SET " + OPEN_INTENTS_IN_NEW_TAB + " = " + 1);
-                        } else {
-                            importDatabase.execSQL("UPDATE " + PREFERENCES_TABLE + " SET " + OPEN_INTENTS_IN_NEW_TAB + " = " + 0);
-                        }
-
-                    // Upgrade from schema version 6, Privacy Browser 3.0.
-                    case 6:
-                        // Add the wide viewport column to the domains table.
-                        importDatabase.execSQL("ALTER TABLE " + DomainsDatabaseHelper.DOMAINS_TABLE + " ADD COLUMN " + DomainsDatabaseHelper.WIDE_VIEWPORT + " INTEGER");
-
-                        // Add the Google Analytics, Facebook Click IDs, Twitter AMP redirects, and wide viewport columns to the preferences table.
-                        importDatabase.execSQL("ALTER TABLE " + PREFERENCES_TABLE + " ADD COLUMN " + GOOGLE_ANALYTICS + " BOOLEAN");
-                        importDatabase.execSQL("ALTER TABLE " + PREFERENCES_TABLE + " ADD COLUMN " + FACEBOOK_CLICK_IDS + " BOOLEAN");
-                        importDatabase.execSQL("ALTER TABLE " + PREFERENCES_TABLE + " ADD COLUMN " + TWITTER_AMP_REDIRECTS + " BOOLEAN");
-                        importDatabase.execSQL("ALTER TABLE " + PREFERENCES_TABLE + " ADD COLUMN " + WIDE_VIEWPORT + " BOOLEAN");
-
-                        // Get the current preference values.
-                        boolean googleAnalytics = sharedPreferences.getBoolean(GOOGLE_ANALYTICS, true);
-                        boolean facebookClickIds = sharedPreferences.getBoolean(FACEBOOK_CLICK_IDS, true);
-                        boolean twitterAmpRedirects = sharedPreferences.getBoolean(TWITTER_AMP_REDIRECTS, true);
-                        boolean wideViewport = sharedPreferences.getBoolean(WIDE_VIEWPORT, true);
-
-                        // Populate the preferences with the current Google Analytics value.
-                        if (googleAnalytics) {
-                            importDatabase.execSQL("UPDATE " + PREFERENCES_TABLE + " SET " + GOOGLE_ANALYTICS + " = " + 1);
-                        } else {
-                            importDatabase.execSQL("UPDATE " + PREFERENCES_TABLE + " SET " + GOOGLE_ANALYTICS + " = " + 0);
-                        }
-
-                        // Populate the preferences with the current Facebook Click IDs value.
-                        if (facebookClickIds) {
-                            importDatabase.execSQL("UPDATE " + PREFERENCES_TABLE + " SET " + FACEBOOK_CLICK_IDS + " = " + 1);
-                        } else {
-                            importDatabase.execSQL("UPDATE " + PREFERENCES_TABLE + " SET " + FACEBOOK_CLICK_IDS + " = " + 0);
-                        }
-
-                        // Populate the preferences table with the current Twitter AMP redirects value.
-                        if (twitterAmpRedirects) {
-                            importDatabase.execSQL("UPDATE " + PREFERENCES_TABLE + " SET " + TWITTER_AMP_REDIRECTS + " = " + 1);
-                        } else {
-                            importDatabase.execSQL("UPDATE " + PREFERENCES_TABLE + " SET " + TWITTER_AMP_REDIRECTS + " = " + 0);
-                        }
-
-                        // Populate the preferences table with the current wide viewport value.
-                        if (wideViewport) {
-                            importDatabase.execSQL("UPDATE " + PREFERENCES_TABLE + " SET " + WIDE_VIEWPORT + " = " + 1);
-                        } else {
-                            importDatabase.execSQL("UPDATE " + PREFERENCES_TABLE + " SET " + WIDE_VIEWPORT + " = " + 0);
-                        }
-
-                    // Upgrade from schema version 7, Privacy Browser 3.1.
-                    case 7:
-                        // Add the UltraList column to the domains table.
-                        importDatabase.execSQL("ALTER TABLE " + DomainsDatabaseHelper.DOMAINS_TABLE + " ADD COLUMN " + DomainsDatabaseHelper.ULTRALIST + " BOOLEAN");
-
-                        // Add the UltraList column to the preferences table.
-                        importDatabase.execSQL("ALTER TABLE " + PREFERENCES_TABLE + " ADD COLUMN " + ULTRALIST + " BOOLEAN");
-
-                        // Get the current preference values.
-                        boolean ultraList = sharedPreferences.getBoolean(ULTRALIST, true);
-
-                        // Populate the preferences tables with the current UltraList value.
-                        if (ultraList) {
-                            importDatabase.execSQL("UPDATE " + DomainsDatabaseHelper.DOMAINS_TABLE + " SET " + DomainsDatabaseHelper.ULTRALIST + " = " + 1);
-                            importDatabase.execSQL("UPDATE " + PREFERENCES_TABLE + " SET " + ULTRALIST + " = " + 1);
-                        } else {
-                            importDatabase.execSQL("UPDATE " + DomainsDatabaseHelper.DOMAINS_TABLE + " SET " + DomainsDatabaseHelper.ULTRALIST + " = " + 0);
-                            importDatabase.execSQL("UPDATE " + PREFERENCES_TABLE + " SET " + ULTRALIST + " = " + 0);
-                        }
-
-                    // Upgrade from schema version 8, Privacy Browser 3.2.
-                    case 8:
-                        // Add the new proxy columns to the preferences table.
-                        importDatabase.execSQL("ALTER TABLE " + PREFERENCES_TABLE + " ADD COLUMN " + PROXY + " TEXT");
-                        importDatabase.execSQL("ALTER TABLE " + PREFERENCES_TABLE + " ADD COLUMN " + PROXY_CUSTOM_URL + " TEXT");
-
-                        // Get the current proxy values.
-                        String proxy = sharedPreferences.getString(PROXY, context.getString(R.string.proxy_default_value));
-                        String proxyCustomUrl = sharedPreferences.getString(PROXY_CUSTOM_URL, context.getString(R.string.proxy_custom_url_default_value));
-
-                        // SQL escape the proxy custom URL string.
-                        proxyCustomUrl = DatabaseUtils.sqlEscapeString(proxyCustomUrl);
-
-                        // Populate the preferences table with the current proxy values. The proxy custom URL does not need to be surrounded by `'` because it was SLQ escaped above.
-                        importDatabase.execSQL("UPDATE " + PREFERENCES_TABLE + " SET " + PROXY + " = '" + proxy + "'");
-                        importDatabase.execSQL("UPDATE " + PREFERENCES_TABLE + " SET " + PROXY_CUSTOM_URL + " = " + proxyCustomUrl);
-
-                    // Upgrade from schema version 9, Privacy Browser 3.3.
-                    case 9:
-                        // Previously this upgrade added `download_location` and `download_custom_location` to the Preferences table.  But they are now removed in schema version 13.
-
-                    // Upgrade from schema version 10, Privacy Browser 3.4.
-                    case 10:
-                        // Add the app theme column to the preferences table.
-                        importDatabase.execSQL("ALTER TABLE " + PREFERENCES_TABLE + " ADD COLUMN " + APP_THEME + " TEXT");
-
-                        // Get a cursor for the dark theme preference.
-                        Cursor darkThemePreferencesCursor = importDatabase.rawQuery("SELECT dark_theme FROM " + PREFERENCES_TABLE, null);
-
-                        // Move to the first preference.
-                        darkThemePreferencesCursor.moveToFirst();
-
-                        // Get the old dark theme value, which is in column 0.
-                        int darkTheme = darkThemePreferencesCursor.getInt(0);
-
-                        // Close the dark theme preference cursor.
-                        darkThemePreferencesCursor.close();
-
-                        // Populate the app theme according to the old dark theme preference.
-                        if (darkTheme == 0) {  // A light theme was selected.
-                            // Set the app theme to be the system default.
-                            importDatabase.execSQL("UPDATE " + PREFERENCES_TABLE + " SET " + APP_THEME + " = 'System default'");
-                        } else {  // A dark theme was selected.
-                            // Set the app theme to be dark.
-                            importDatabase.execSQL("UPDATE " + PREFERENCES_TABLE + " SET " + APP_THEME + " = 'Dark'");
-                        }
-
-                        // Add the WebView theme to the domains table.  This defaults to 0, which is `System default`, so a separate step isn't needed to populate the database.
-                        importDatabase.execSQL("ALTER TABLE " + DomainsDatabaseHelper.DOMAINS_TABLE + " ADD COLUMN " + DomainsDatabaseHelper.WEBVIEW_THEME + " INTEGER");
-
-                        // Add the WebView theme to the preferences table.
-                        importDatabase.execSQL("ALTER TABLE " + PREFERENCES_TABLE + " ADD COLUMN " + WEBVIEW_THEME + " TEXT");
-
-                        // Get the WebView theme default value string.
-                        String webViewThemeDefaultValue = context.getString(R.string.webview_theme_default_value);
-
-                        // Set the WebView theme in the preferences table to be the default.
-                        importDatabase.execSQL("UPDATE " + PREFERENCES_TABLE + " SET " + WEBVIEW_THEME + " = " + "'" + webViewThemeDefaultValue + "'");
-
-                    // Upgrade from schema version 11, Privacy Browser 3.5.
-                    case 11:
-                        // Add the clear logcat column to the preferences table.
-                        importDatabase.execSQL("ALTER TABLE " + PREFERENCES_TABLE + " ADD COLUMN " + CLEAR_LOGCAT + " BOOLEAN");
-
-                        // Get the current clear logcat value.
-                        boolean clearLogcat = sharedPreferences.getBoolean(CLEAR_LOGCAT, true);
-
-                        // Populate the preferences table with the current clear logcat value.
-                        if (clearLogcat) {
-                            importDatabase.execSQL("UPDATE " + PREFERENCES_TABLE + " SET " + CLEAR_LOGCAT + " = " + 1);
-                        } else {
-                            importDatabase.execSQL("UPDATE " + PREFERENCES_TABLE + " SET " + CLEAR_LOGCAT + " = " + 0);
-                        }
-
-                    // Upgrade from schema version 12, Privacy Browser 3.6.
-                    case 12:
-                        // Do nothing.  `download_location` and `download_custom_location` were removed from the preferences table.
-
-                    // Upgrade from schema version 13, Privacy Browser 3.7
-                    case 13:
-                        // `enabledthirdpartycookies` was removed from the domains table.  `do_not_track` and `third_party_cookies` were removed from the preferences table.
-
-                        // Once the SQLite version is >= 3.25.0 `ALTER TABLE RENAME COLUMN` can be used.  <https://www.sqlite.org/lang_altertable.html> <https://www.sqlite.org/changes.html>
-                        // <https://developer.android.com/reference/android/database/sqlite/package-summary>
-                        // In the meantime, a new column must be created with the new name.  There is no need to delete the old column on the temporary import database.
-
-                        // Create the new cookies columns.
-                        importDatabase.execSQL("ALTER TABLE " + DomainsDatabaseHelper.DOMAINS_TABLE + " ADD COLUMN " + DomainsDatabaseHelper.COOKIES + " BOOLEAN");
-                        importDatabase.execSQL("ALTER TABLE " + PREFERENCES_TABLE + " ADD COLUMN " + COOKIES + " BOOLEAN");
-
-                        // Copy the data from the old cookies columns to the new ones.
-                        importDatabase.execSQL("UPDATE " + DomainsDatabaseHelper.DOMAINS_TABLE + " SET " + DomainsDatabaseHelper.COOKIES + " = enablefirstpartycookies");
-                        importDatabase.execSQL("UPDATE " + PREFERENCES_TABLE + " SET " + COOKIES + " = first_party_cookies");
-
-                        // Create the new columns.
-                        importDatabase.execSQL("ALTER TABLE " + PREFERENCES_TABLE + " ADD COLUMN " + DOWNLOAD_WITH_EXTERNAL_APP + " BOOLEAN");
-                        importDatabase.execSQL("ALTER TABLE " + PREFERENCES_TABLE + " ADD COLUMN " + BOTTOM_APP_BAR + " BOOLEAN");
-
-                        // Get the current values for the new columns.
-                        boolean downloadWithExternalApp = sharedPreferences.getBoolean(DOWNLOAD_WITH_EXTERNAL_APP, false);
-                        boolean bottomAppBar = sharedPreferences.getBoolean(BOTTOM_APP_BAR, false);
-
-                        // Populate the preferences table with the current download with external app value.
-                        if (downloadWithExternalApp) {
-                            importDatabase.execSQL("UPDATE " + PREFERENCES_TABLE + " SET " + DOWNLOAD_WITH_EXTERNAL_APP + " = " + 1);
-                        } else {
-                            importDatabase.execSQL("UPDATE " + PREFERENCES_TABLE + " SET " + DOWNLOAD_WITH_EXTERNAL_APP + " = " + 0);
-                        }
-
-                        // Populate the preferences table with the current bottom app bar value.
-                        if (bottomAppBar) {
-                            importDatabase.execSQL("UPDATE " + PREFERENCES_TABLE + " SET " + BOTTOM_APP_BAR + " = " + 1);
-                        } else {
-                            importDatabase.execSQL("UPDATE " + PREFERENCES_TABLE + " SET " + BOTTOM_APP_BAR + " = " + 0);
-                        }
-                }
-            }
-
-            // Get a cursor for the bookmarks table.
-            Cursor importBookmarksCursor = importDatabase.rawQuery("SELECT * FROM " + BookmarksDatabaseHelper.BOOKMARKS_TABLE, null);
-
-            // Delete the current bookmarks database.
-            context.deleteDatabase(BookmarksDatabaseHelper.BOOKMARKS_DATABASE);
-
-            // Create a new bookmarks database.
-            BookmarksDatabaseHelper bookmarksDatabaseHelper = new BookmarksDatabaseHelper(context);
-
-            // Move to the first bookmark.
-            importBookmarksCursor.moveToFirst();
-
-            // Copy the data from the import bookmarks cursor into the bookmarks database.
-            for (int i = 0; i < importBookmarksCursor.getCount(); i++) {
-                // Extract the record from the cursor and store the data in a ContentValues.
-                ContentValues bookmarksContentValues = new ContentValues();
-                bookmarksContentValues.put(BookmarksDatabaseHelper.BOOKMARK_NAME, importBookmarksCursor.getString(importBookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_NAME)));
-                bookmarksContentValues.put(BookmarksDatabaseHelper.BOOKMARK_URL, importBookmarksCursor.getString(importBookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_URL)));
-                bookmarksContentValues.put(BookmarksDatabaseHelper.PARENT_FOLDER, importBookmarksCursor.getString(importBookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.PARENT_FOLDER)));
-                bookmarksContentValues.put(BookmarksDatabaseHelper.DISPLAY_ORDER, importBookmarksCursor.getInt(importBookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.DISPLAY_ORDER)));
-                bookmarksContentValues.put(BookmarksDatabaseHelper.IS_FOLDER, importBookmarksCursor.getInt(importBookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.IS_FOLDER)));
-                bookmarksContentValues.put(BookmarksDatabaseHelper.FAVORITE_ICON, importBookmarksCursor.getBlob(importBookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.FAVORITE_ICON)));
-
-                // Insert the record into the export database.
-                bookmarksDatabaseHelper.createBookmark(bookmarksContentValues);
-
-                // Advance to the next record.
-                importBookmarksCursor.moveToNext();
-            }
-
-            // Close the bookmarks cursor.
-            importBookmarksCursor.close();
-
-            // Close the bookmarks database.
-            bookmarksDatabaseHelper.close();
-
-
-            // Get a cursor for the domains table.
-            Cursor importDomainsCursor = importDatabase.rawQuery("SELECT * FROM " + DomainsDatabaseHelper.DOMAINS_TABLE + " ORDER BY " + DomainsDatabaseHelper.DOMAIN_NAME + " ASC", null);
-
-            // Delete the current domains database.
-            context.deleteDatabase(DomainsDatabaseHelper.DOMAINS_DATABASE);
-
-            // Create a new domains database.
-            DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(context);
-
-            // Move to the first domain.
-            importDomainsCursor.moveToFirst();
-
-            // Copy the data from the import domains cursor into the domains database.
-            for (int i = 0; i < importDomainsCursor.getCount(); i++) {
-                // Extract the record from the cursor and store the data in a ContentValues.
-                ContentValues domainsContentValues = new ContentValues();
-                domainsContentValues.put(DomainsDatabaseHelper.DOMAIN_NAME, importDomainsCursor.getString(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DOMAIN_NAME)));
-                domainsContentValues.put(DomainsDatabaseHelper.ENABLE_JAVASCRIPT, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_JAVASCRIPT)));
-                domainsContentValues.put(DomainsDatabaseHelper.COOKIES, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.COOKIES)));
-                domainsContentValues.put(DomainsDatabaseHelper.ENABLE_DOM_STORAGE, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_DOM_STORAGE)));
-                domainsContentValues.put(DomainsDatabaseHelper.ENABLE_FORM_DATA, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FORM_DATA)));
-                domainsContentValues.put(DomainsDatabaseHelper.ENABLE_EASYLIST, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_EASYLIST)));
-                domainsContentValues.put(DomainsDatabaseHelper.ENABLE_EASYPRIVACY, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_EASYPRIVACY)));
-                domainsContentValues.put(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST,
-                        importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST)));
-                domainsContentValues.put(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST,
-                        importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST)));
-                domainsContentValues.put(DomainsDatabaseHelper.ULTRALIST, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ULTRALIST)));
-                domainsContentValues.put(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY)));
-                domainsContentValues.put(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS,
-                        importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS)));
-                domainsContentValues.put(DomainsDatabaseHelper.USER_AGENT, importDomainsCursor.getString(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.USER_AGENT)));
-                domainsContentValues.put(DomainsDatabaseHelper.FONT_SIZE, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.FONT_SIZE)));
-                domainsContentValues.put(DomainsDatabaseHelper.SWIPE_TO_REFRESH, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SWIPE_TO_REFRESH)));
-                domainsContentValues.put(DomainsDatabaseHelper.WEBVIEW_THEME, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.WEBVIEW_THEME)));
-                domainsContentValues.put(DomainsDatabaseHelper.WIDE_VIEWPORT, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.WIDE_VIEWPORT)));
-                domainsContentValues.put(DomainsDatabaseHelper.DISPLAY_IMAGES, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DISPLAY_IMAGES)));
-                domainsContentValues.put(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE)));
-                domainsContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME,
-                        importDomainsCursor.getString(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME)));
-                domainsContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION,
-                        importDomainsCursor.getString(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION)));
-                domainsContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT,
-                        importDomainsCursor.getString(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT)));
-                domainsContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME,
-                        importDomainsCursor.getString(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME)));
-                domainsContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION,
-                        importDomainsCursor.getString(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION)));
-                domainsContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT,
-                        importDomainsCursor.getString(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT)));
-                domainsContentValues.put(DomainsDatabaseHelper.SSL_START_DATE, importDomainsCursor.getLong(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_START_DATE)));
-                domainsContentValues.put(DomainsDatabaseHelper.SSL_END_DATE, importDomainsCursor.getLong(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_END_DATE)));
-                domainsContentValues.put(DomainsDatabaseHelper.PINNED_IP_ADDRESSES, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.PINNED_IP_ADDRESSES)));
-                domainsContentValues.put(DomainsDatabaseHelper.IP_ADDRESSES, importDomainsCursor.getString(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.IP_ADDRESSES)));
-
-                // Insert the record into the export database.
-                domainsDatabaseHelper.addDomain(domainsContentValues);
-
-                // Advance to the next record.
-                importDomainsCursor.moveToNext();
-            }
-
-            // Close the domains cursor.
-            importDomainsCursor.close();
-
-            // Close the domains database.
-            domainsDatabaseHelper.close();
-
-
-            // Get a cursor for the preferences table.
-            Cursor importPreferencesCursor = importDatabase.rawQuery("SELECT * FROM " + PREFERENCES_TABLE, null);
-
-            // Move to the first preference.
-            importPreferencesCursor.moveToFirst();
-
-            // Import the preference data.
-            sharedPreferences.edit()
-                    .putBoolean(JAVASCRIPT, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(JAVASCRIPT)) == 1)
-                    .putBoolean(COOKIES, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(COOKIES)) == 1)
-                    .putBoolean(DOM_STORAGE, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(DOM_STORAGE)) == 1)
-                    // Save form data can be removed once the minimum API >= 26.
-                    .putBoolean(SAVE_FORM_DATA, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(SAVE_FORM_DATA)) == 1)
-                    .putString(USER_AGENT, importPreferencesCursor.getString(importPreferencesCursor.getColumnIndexOrThrow(USER_AGENT)))
-                    .putString(CUSTOM_USER_AGENT, importPreferencesCursor.getString(importPreferencesCursor.getColumnIndexOrThrow(CUSTOM_USER_AGENT)))
-                    .putBoolean(INCOGNITO_MODE, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(INCOGNITO_MODE)) == 1)
-                    .putBoolean(ALLOW_SCREENSHOTS, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(ALLOW_SCREENSHOTS)) == 1)
-                    .putBoolean(EASYLIST, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(EASYLIST)) == 1)
-                    .putBoolean(EASYPRIVACY, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(EASYPRIVACY)) == 1)
-                    .putBoolean(FANBOYS_ANNOYANCE_LIST, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(FANBOYS_ANNOYANCE_LIST)) == 1)
-                    .putBoolean(FANBOYS_SOCIAL_BLOCKING_LIST, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(FANBOYS_SOCIAL_BLOCKING_LIST)) == 1)
-                    .putBoolean(ULTRALIST, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(ULTRALIST)) == 1)
-                    .putBoolean(ULTRAPRIVACY, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(ULTRAPRIVACY)) == 1)
-                    .putBoolean(BLOCK_ALL_THIRD_PARTY_REQUESTS, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(BLOCK_ALL_THIRD_PARTY_REQUESTS)) == 1)
-                    .putBoolean(GOOGLE_ANALYTICS, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(GOOGLE_ANALYTICS)) == 1)
-                    .putBoolean(FACEBOOK_CLICK_IDS, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(FACEBOOK_CLICK_IDS)) == 1)
-                    .putBoolean(TWITTER_AMP_REDIRECTS, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(TWITTER_AMP_REDIRECTS)) == 1)
-                    .putString(SEARCH, importPreferencesCursor.getString(importPreferencesCursor.getColumnIndexOrThrow(SEARCH)))
-                    .putString(SEARCH_CUSTOM_URL, importPreferencesCursor.getString(importPreferencesCursor.getColumnIndexOrThrow(SEARCH_CUSTOM_URL)))
-                    .putString(PROXY, importPreferencesCursor.getString(importPreferencesCursor.getColumnIndexOrThrow(PROXY)))
-                    .putString(PROXY_CUSTOM_URL, importPreferencesCursor.getString(importPreferencesCursor.getColumnIndexOrThrow(PROXY_CUSTOM_URL)))
-                    .putBoolean(FULL_SCREEN_BROWSING_MODE, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(FULL_SCREEN_BROWSING_MODE)) == 1)
-                    .putBoolean(HIDE_APP_BAR, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(HIDE_APP_BAR)) == 1)
-                    .putBoolean(CLEAR_EVERYTHING, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(CLEAR_EVERYTHING)) == 1)
-                    .putBoolean(CLEAR_COOKIES, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(CLEAR_COOKIES)) == 1)
-                    .putBoolean(CLEAR_DOM_STORAGE, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(CLEAR_DOM_STORAGE)) == 1)
-                    // Clear form data can be removed once the minimum API >= 26.
-                    .putBoolean(CLEAR_FORM_DATA, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(CLEAR_FORM_DATA)) == 1)
-                    .putBoolean(CLEAR_LOGCAT, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(CLEAR_LOGCAT)) == 1)
-                    .putBoolean(CLEAR_CACHE, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(CLEAR_CACHE)) == 1)
-                    .putString(HOMEPAGE, importPreferencesCursor.getString(importPreferencesCursor.getColumnIndexOrThrow(HOMEPAGE)))
-                    .putString(FONT_SIZE, importPreferencesCursor.getString(importPreferencesCursor.getColumnIndexOrThrow(FONT_SIZE)))
-                    .putBoolean(OPEN_INTENTS_IN_NEW_TAB, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(OPEN_INTENTS_IN_NEW_TAB)) == 1)
-                    .putBoolean(SWIPE_TO_REFRESH, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(SWIPE_TO_REFRESH)) == 1)
-                    .putBoolean(DOWNLOAD_WITH_EXTERNAL_APP, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(DOWNLOAD_WITH_EXTERNAL_APP)) == 1)
-                    .putBoolean(SCROLL_APP_BAR, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(SCROLL_APP_BAR)) == 1)
-                    .putBoolean(BOTTOM_APP_BAR, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(BOTTOM_APP_BAR)) == 1)
-                    .putBoolean(DISPLAY_ADDITIONAL_APP_BAR_ICONS, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(DISPLAY_ADDITIONAL_APP_BAR_ICONS)) == 1)
-                    .putString(APP_THEME, importPreferencesCursor.getString(importPreferencesCursor.getColumnIndexOrThrow(APP_THEME)))
-                    .putString(WEBVIEW_THEME, importPreferencesCursor.getString(importPreferencesCursor.getColumnIndexOrThrow(WEBVIEW_THEME)))
-                    .putBoolean(WIDE_VIEWPORT, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(WIDE_VIEWPORT)) == 1)
-                    .putBoolean(DISPLAY_WEBPAGE_IMAGES, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(DISPLAY_WEBPAGE_IMAGES)) == 1)
-                    .apply();
-
-            // Close the preferences cursor.
-            importPreferencesCursor.close();
-
-            // Close the import database.
-            importDatabase.close();
-
-            // Delete the temporary import file database, journal, and other related auxiliary files.
-            SQLiteDatabase.deleteDatabase(temporaryImportFile);
-
-            // Import successful.
-            return IMPORT_SUCCESSFUL;
-        } catch (Exception exception) {
-            // Return the import error.
-            return exception.toString();
-        }
-    }
-
-    public String exportUnencrypted(OutputStream exportFileOutputStream, Context context) {
-        try {
-            // Create a temporary export file.
-            File temporaryExportFile = File.createTempFile("temporary_export_file", null, context.getCacheDir());
-
-            // Create the temporary export database.
-            SQLiteDatabase temporaryExportDatabase = SQLiteDatabase.openOrCreateDatabase(temporaryExportFile, null);
-
-            // Set the temporary export database version number.
-            temporaryExportDatabase.setVersion(SCHEMA_VERSION);
-
-
-            // Create the temporary export database bookmarks table.
-            temporaryExportDatabase.execSQL(BookmarksDatabaseHelper.CREATE_BOOKMARKS_TABLE);
-
-            // Open the bookmarks database.
-            BookmarksDatabaseHelper bookmarksDatabaseHelper = new BookmarksDatabaseHelper(context);
-
-            // Get a full bookmarks cursor.
-            Cursor bookmarksCursor = bookmarksDatabaseHelper.getAllBookmarks();
-
-            // Move to the first bookmark.
-            bookmarksCursor.moveToFirst();
-
-            // Copy the data from the bookmarks cursor into the export database.
-            for (int i = 0; i < bookmarksCursor.getCount(); i++) {
-                // Extract the record from the cursor and store the data in a ContentValues.
-                ContentValues bookmarksContentValues = new ContentValues();
-                bookmarksContentValues.put(BookmarksDatabaseHelper.BOOKMARK_NAME, bookmarksCursor.getString(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_NAME)));
-                bookmarksContentValues.put(BookmarksDatabaseHelper.BOOKMARK_URL, bookmarksCursor.getString(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_URL)));
-                bookmarksContentValues.put(BookmarksDatabaseHelper.PARENT_FOLDER, bookmarksCursor.getString(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.PARENT_FOLDER)));
-                bookmarksContentValues.put(BookmarksDatabaseHelper.DISPLAY_ORDER, bookmarksCursor.getInt(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.DISPLAY_ORDER)));
-                bookmarksContentValues.put(BookmarksDatabaseHelper.IS_FOLDER, bookmarksCursor.getInt(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.IS_FOLDER)));
-                bookmarksContentValues.put(BookmarksDatabaseHelper.FAVORITE_ICON, bookmarksCursor.getBlob(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.FAVORITE_ICON)));
-
-                // Insert the record into the temporary export database.
-                temporaryExportDatabase.insert(BookmarksDatabaseHelper.BOOKMARKS_TABLE, null, bookmarksContentValues);
-
-                // Advance to the next record.
-                bookmarksCursor.moveToNext();
-            }
-
-            // Close the bookmarks database.
-            bookmarksCursor.close();
-            bookmarksDatabaseHelper.close();
-
-
-            // Create the temporary export database domains table.
-            temporaryExportDatabase.execSQL(DomainsDatabaseHelper.CREATE_DOMAINS_TABLE);
-
-            // Open the domains database.
-            DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(context);
-
-            // Get a full domains database cursor.
-            Cursor domainsCursor = domainsDatabaseHelper.getCompleteCursorOrderedByDomain();
-
-            // Move to the first domain.
-            domainsCursor.moveToFirst();
-
-            // Copy the data from the domains cursor into the export database.
-            for (int i = 0; i < domainsCursor.getCount(); i++) {
-                // Extract the record from the cursor and store the data in a ContentValues.
-                ContentValues domainsContentValues = new ContentValues();
-                domainsContentValues.put(DomainsDatabaseHelper.DOMAIN_NAME, domainsCursor.getString(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DOMAIN_NAME)));
-                domainsContentValues.put(DomainsDatabaseHelper.ENABLE_JAVASCRIPT, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_JAVASCRIPT)));
-                domainsContentValues.put(DomainsDatabaseHelper.COOKIES, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.COOKIES)));
-                domainsContentValues.put(DomainsDatabaseHelper.ENABLE_DOM_STORAGE, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_DOM_STORAGE)));
-                domainsContentValues.put(DomainsDatabaseHelper.ENABLE_FORM_DATA, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FORM_DATA)));
-                domainsContentValues.put(DomainsDatabaseHelper.ENABLE_EASYLIST, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_EASYLIST)));
-                domainsContentValues.put(DomainsDatabaseHelper.ENABLE_EASYPRIVACY, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_EASYPRIVACY)));
-                domainsContentValues.put(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST)));
-                domainsContentValues.put(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST,
-                        domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST)));
-                domainsContentValues.put(DomainsDatabaseHelper.ULTRALIST, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ULTRALIST)));
-                domainsContentValues.put(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY)));
-                domainsContentValues.put(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS)));
-                domainsContentValues.put(DomainsDatabaseHelper.USER_AGENT, domainsCursor.getString(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.USER_AGENT)));
-                domainsContentValues.put(DomainsDatabaseHelper.FONT_SIZE, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.FONT_SIZE)));
-                domainsContentValues.put(DomainsDatabaseHelper.SWIPE_TO_REFRESH, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SWIPE_TO_REFRESH)));
-                domainsContentValues.put(DomainsDatabaseHelper.WEBVIEW_THEME, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.WEBVIEW_THEME)));
-                domainsContentValues.put(DomainsDatabaseHelper.WIDE_VIEWPORT, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.WIDE_VIEWPORT)));
-                domainsContentValues.put(DomainsDatabaseHelper.DISPLAY_IMAGES, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DISPLAY_IMAGES)));
-                domainsContentValues.put(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE)));
-                domainsContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME, domainsCursor.getString(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME)));
-                domainsContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION, domainsCursor.getString(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION)));
-                domainsContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT,
-                        domainsCursor.getString(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT)));
-                domainsContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME, domainsCursor.getString(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME)));
-                domainsContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION, domainsCursor.getString(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION)));
-                domainsContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT,
-                        domainsCursor.getString(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT)));
-                domainsContentValues.put(DomainsDatabaseHelper.SSL_START_DATE, domainsCursor.getLong(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_START_DATE)));
-                domainsContentValues.put(DomainsDatabaseHelper.SSL_END_DATE, domainsCursor.getLong(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_END_DATE)));
-                domainsContentValues.put(DomainsDatabaseHelper.PINNED_IP_ADDRESSES, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.PINNED_IP_ADDRESSES)));
-                domainsContentValues.put(DomainsDatabaseHelper.IP_ADDRESSES, domainsCursor.getString(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.IP_ADDRESSES)));
-
-                // Insert the record into the temporary export database.
-                temporaryExportDatabase.insert(DomainsDatabaseHelper.DOMAINS_TABLE, null, domainsContentValues);
-
-                // Advance to the next record.
-                domainsCursor.moveToNext();
-            }
-
-            // Close the domains database.
-            domainsCursor.close();
-            domainsDatabaseHelper.close();
-
-
-            // Prepare the preferences table SQL creation string.
-            String CREATE_PREFERENCES_TABLE = "CREATE TABLE " + PREFERENCES_TABLE + " (" +
-                    _ID + " INTEGER PRIMARY KEY, " +
-                    JAVASCRIPT + " BOOLEAN, " +
-                    COOKIES + " BOOLEAN, " +
-                    DOM_STORAGE + " BOOLEAN, " +
-                    SAVE_FORM_DATA + " BOOLEAN, " +
-                    USER_AGENT + " TEXT, " +
-                    CUSTOM_USER_AGENT + " TEXT, " +
-                    INCOGNITO_MODE + " BOOLEAN, " +
-                    ALLOW_SCREENSHOTS + " BOOLEAN, " +
-                    EASYLIST + " BOOLEAN, " +
-                    EASYPRIVACY + " BOOLEAN, " +
-                    FANBOYS_ANNOYANCE_LIST + " BOOLEAN, " +
-                    FANBOYS_SOCIAL_BLOCKING_LIST + " BOOLEAN, " +
-                    ULTRALIST + " BOOLEAN, " +
-                    ULTRAPRIVACY + " BOOLEAN, " +
-                    BLOCK_ALL_THIRD_PARTY_REQUESTS + " BOOLEAN, " +
-                    GOOGLE_ANALYTICS + " BOOLEAN, " +
-                    FACEBOOK_CLICK_IDS + " BOOLEAN, " +
-                    TWITTER_AMP_REDIRECTS + " BOOLEAN, " +
-                    SEARCH + " TEXT, " +
-                    SEARCH_CUSTOM_URL + " TEXT, " +
-                    PROXY + " TEXT, " +
-                    PROXY_CUSTOM_URL + " TEXT, " +
-                    FULL_SCREEN_BROWSING_MODE + " BOOLEAN, " +
-                    HIDE_APP_BAR + " BOOLEAN, " +
-                    CLEAR_EVERYTHING + " BOOLEAN, " +
-                    CLEAR_COOKIES + " BOOLEAN, " +
-                    CLEAR_DOM_STORAGE + " BOOLEAN, " +
-                    CLEAR_FORM_DATA + " BOOLEAN, " +
-                    CLEAR_LOGCAT + " BOOLEAN, " +
-                    CLEAR_CACHE + " BOOLEAN, " +
-                    HOMEPAGE + " TEXT, " +
-                    FONT_SIZE + " TEXT, " +
-                    OPEN_INTENTS_IN_NEW_TAB + " BOOLEAN, " +
-                    SWIPE_TO_REFRESH + " BOOLEAN, " +
-                    DOWNLOAD_WITH_EXTERNAL_APP + " BOOLEAN, " +
-                    SCROLL_APP_BAR + " BOOLEAN, " +
-                    BOTTOM_APP_BAR + " BOOLEAN, " +
-                    DISPLAY_ADDITIONAL_APP_BAR_ICONS + " BOOLEAN, " +
-                    APP_THEME + " TEXT, " +
-                    WEBVIEW_THEME + " TEXT, " +
-                    WIDE_VIEWPORT + " BOOLEAN, " +
-                    DISPLAY_WEBPAGE_IMAGES + " BOOLEAN)";
-
-            // Create the temporary export database preferences table.
-            temporaryExportDatabase.execSQL(CREATE_PREFERENCES_TABLE);
-
-            // Get a handle for the shared preference.
-            SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
-
-            // Create a ContentValues with the preferences information.
-            ContentValues preferencesContentValues = new ContentValues();
-            preferencesContentValues.put(JAVASCRIPT, sharedPreferences.getBoolean(JAVASCRIPT, false));
-            preferencesContentValues.put(COOKIES, sharedPreferences.getBoolean(COOKIES, false));
-            preferencesContentValues.put(DOM_STORAGE, sharedPreferences.getBoolean(DOM_STORAGE, false));
-            preferencesContentValues.put(SAVE_FORM_DATA, sharedPreferences.getBoolean(SAVE_FORM_DATA, false));  // Save form data can be removed once the minimum API >= 26.
-            preferencesContentValues.put(USER_AGENT, sharedPreferences.getString(USER_AGENT, context.getString(R.string.user_agent_default_value)));
-            preferencesContentValues.put(CUSTOM_USER_AGENT, sharedPreferences.getString(CUSTOM_USER_AGENT, context.getString(R.string.custom_user_agent_default_value)));
-            preferencesContentValues.put(INCOGNITO_MODE, sharedPreferences.getBoolean(INCOGNITO_MODE, false));
-            preferencesContentValues.put(ALLOW_SCREENSHOTS, sharedPreferences.getBoolean(ALLOW_SCREENSHOTS, false));
-            preferencesContentValues.put(EASYLIST, sharedPreferences.getBoolean(EASYLIST, true));
-            preferencesContentValues.put(EASYPRIVACY, sharedPreferences.getBoolean(EASYPRIVACY, true));
-            preferencesContentValues.put(FANBOYS_ANNOYANCE_LIST, sharedPreferences.getBoolean(FANBOYS_ANNOYANCE_LIST, true));
-            preferencesContentValues.put(FANBOYS_SOCIAL_BLOCKING_LIST, sharedPreferences.getBoolean(FANBOYS_SOCIAL_BLOCKING_LIST, true));
-            preferencesContentValues.put(ULTRALIST, sharedPreferences.getBoolean(ULTRALIST, true));
-            preferencesContentValues.put(ULTRAPRIVACY, sharedPreferences.getBoolean(ULTRAPRIVACY, true));
-            preferencesContentValues.put(BLOCK_ALL_THIRD_PARTY_REQUESTS, sharedPreferences.getBoolean(BLOCK_ALL_THIRD_PARTY_REQUESTS, false));
-            preferencesContentValues.put(GOOGLE_ANALYTICS, sharedPreferences.getBoolean(GOOGLE_ANALYTICS, true));
-            preferencesContentValues.put(FACEBOOK_CLICK_IDS, sharedPreferences.getBoolean(FACEBOOK_CLICK_IDS, true));
-            preferencesContentValues.put(TWITTER_AMP_REDIRECTS, sharedPreferences.getBoolean(TWITTER_AMP_REDIRECTS, true));
-            preferencesContentValues.put(SEARCH, sharedPreferences.getString(SEARCH, context.getString(R.string.search_default_value)));
-            preferencesContentValues.put(SEARCH_CUSTOM_URL, sharedPreferences.getString(SEARCH_CUSTOM_URL, context.getString(R.string.search_custom_url_default_value)));
-            preferencesContentValues.put(PROXY, sharedPreferences.getString(PROXY, context.getString(R.string.proxy_default_value)));
-            preferencesContentValues.put(PROXY_CUSTOM_URL, sharedPreferences.getString(PROXY_CUSTOM_URL, context.getString(R.string.proxy_custom_url_default_value)));
-            preferencesContentValues.put(FULL_SCREEN_BROWSING_MODE, sharedPreferences.getBoolean(FULL_SCREEN_BROWSING_MODE, false));
-            preferencesContentValues.put(HIDE_APP_BAR, sharedPreferences.getBoolean(HIDE_APP_BAR, true));
-            preferencesContentValues.put(CLEAR_EVERYTHING, sharedPreferences.getBoolean(CLEAR_EVERYTHING, true));
-            preferencesContentValues.put(CLEAR_COOKIES, sharedPreferences.getBoolean(CLEAR_COOKIES, true));
-            preferencesContentValues.put(CLEAR_DOM_STORAGE, sharedPreferences.getBoolean(CLEAR_DOM_STORAGE, true));
-            preferencesContentValues.put(CLEAR_FORM_DATA, sharedPreferences.getBoolean(CLEAR_FORM_DATA, true));  // Clear form data can be removed once the minimum API >= 26.
-            preferencesContentValues.put(CLEAR_LOGCAT, sharedPreferences.getBoolean(CLEAR_LOGCAT, true));
-            preferencesContentValues.put(CLEAR_CACHE, sharedPreferences.getBoolean(CLEAR_CACHE, true));
-            preferencesContentValues.put(HOMEPAGE, sharedPreferences.getString(HOMEPAGE, context.getString(R.string.homepage_default_value)));
-            preferencesContentValues.put(FONT_SIZE, sharedPreferences.getString(FONT_SIZE, context.getString(R.string.font_size_default_value)));
-            preferencesContentValues.put(OPEN_INTENTS_IN_NEW_TAB, sharedPreferences.getBoolean(OPEN_INTENTS_IN_NEW_TAB, true));
-            preferencesContentValues.put(SWIPE_TO_REFRESH, sharedPreferences.getBoolean(SWIPE_TO_REFRESH, true));
-            preferencesContentValues.put(DOWNLOAD_WITH_EXTERNAL_APP, sharedPreferences.getBoolean(DOWNLOAD_WITH_EXTERNAL_APP, false));
-            preferencesContentValues.put(SCROLL_APP_BAR, sharedPreferences.getBoolean(SCROLL_APP_BAR, true));
-            preferencesContentValues.put(BOTTOM_APP_BAR, sharedPreferences.getBoolean(BOTTOM_APP_BAR, false));
-            preferencesContentValues.put(DISPLAY_ADDITIONAL_APP_BAR_ICONS, sharedPreferences.getBoolean(DISPLAY_ADDITIONAL_APP_BAR_ICONS, false));
-            preferencesContentValues.put(APP_THEME, sharedPreferences.getString(APP_THEME, context.getString(R.string.app_theme_default_value)));
-            preferencesContentValues.put(WEBVIEW_THEME, sharedPreferences.getString(WEBVIEW_THEME, context.getString(R.string.webview_theme_default_value)));
-            preferencesContentValues.put(WIDE_VIEWPORT, sharedPreferences.getBoolean(WIDE_VIEWPORT, true));
-            preferencesContentValues.put(DISPLAY_WEBPAGE_IMAGES, sharedPreferences.getBoolean(DISPLAY_WEBPAGE_IMAGES, true));
-
-            // Insert the preferences into the temporary export database.
-            temporaryExportDatabase.insert(PREFERENCES_TABLE, null, preferencesContentValues);
-
-            // Close the temporary export database.
-            temporaryExportDatabase.close();
-
-
-            // Create the temporary export file input stream.
-            FileInputStream temporaryExportFileInputStream = new FileInputStream(temporaryExportFile);
-
-            // Create a byte array.
-            byte[] transferByteArray = new byte[1024];
-
-            // Create an integer to track the number of bytes read.
-            int bytesRead;
-
-            // Copy the temporary export file to the export file output stream.
-            while ((bytesRead = temporaryExportFileInputStream.read(transferByteArray)) > 0) {
-                exportFileOutputStream.write(transferByteArray, 0, bytesRead);
-            }
-
-            // Flush the export file output stream.
-            exportFileOutputStream.flush();
-
-            // Close the file streams.
-            temporaryExportFileInputStream.close();
-            exportFileOutputStream.close();
-
-            // Delete the temporary export file database, journal, and other related auxiliary files.
-            SQLiteDatabase.deleteDatabase(temporaryExportFile);
-
-            // Export successful.
-            return EXPORT_SUCCESSFUL;
-        } catch (Exception exception) {
-            // Return the export error.
-            return exception.toString();
-        }
-    }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/stoutner/privacybrowser/helpers/ImportExportDatabaseHelper.kt b/app/src/main/java/com/stoutner/privacybrowser/helpers/ImportExportDatabaseHelper.kt
new file mode 100644 (file)
index 0000000..13abb32
--- /dev/null
@@ -0,0 +1,829 @@
+/*
+ * Copyright © 2018-2022 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.helpers
+
+import android.content.ContentValues
+import android.content.Context
+import android.database.DatabaseUtils
+import android.database.sqlite.SQLiteDatabase
+
+import androidx.preference.PreferenceManager
+
+import com.stoutner.privacybrowser.R
+
+import java.io.File
+import java.io.FileInputStream
+import java.io.FileOutputStream
+import java.io.InputStream
+import java.io.OutputStream
+
+// Define the private class constants.
+private const val SCHEMA_VERSION = 14
+private const val PREFERENCES_TABLE = "preferences"
+
+// Define the private preferences constants.
+private const val ID = "_id"
+private const val JAVASCRIPT = "javascript"
+private const val COOKIES = "cookies"
+private const val DOM_STORAGE = "dom_storage"
+private const val SAVE_FORM_DATA = "save_form_data"
+private const val USER_AGENT = "user_agent"
+private const val CUSTOM_USER_AGENT = "custom_user_agent"
+private const val INCOGNITO_MODE = "incognito_mode"
+private const val ALLOW_SCREENSHOTS = "allow_screenshots"
+private const val EASYLIST = "easylist"
+private const val EASYPRIVACY = "easyprivacy"
+private const val FANBOYS_ANNOYANCE_LIST = "fanboys_annoyance_list"
+private const val FANBOYS_SOCIAL_BLOCKING_LIST = "fanboys_social_blocking_list"
+private const val ULTRALIST = "ultralist"
+private const val ULTRAPRIVACY = "ultraprivacy"
+private const val BLOCK_ALL_THIRD_PARTY_REQUESTS = "block_all_third_party_requests"
+private const val GOOGLE_ANALYTICS = "google_analytics"
+private const val FACEBOOK_CLICK_IDS = "facebook_click_ids"
+private const val TWITTER_AMP_REDIRECTS = "twitter_amp_redirects"
+private const val SEARCH = "search"
+private const val SEARCH_CUSTOM_URL = "search_custom_url"
+private const val PROXY = "proxy"
+private const val PROXY_CUSTOM_URL = "proxy_custom_url"
+private const val FULL_SCREEN_BROWSING_MODE = "full_screen_browsing_mode"
+private const val HIDE_APP_BAR = "hide_app_bar"
+private const val CLEAR_EVERYTHING = "clear_everything"
+private const val CLEAR_COOKIES = "clear_cookies"
+private const val CLEAR_DOM_STORAGE = "clear_dom_storage"
+private const val CLEAR_FORM_DATA = "clear_form_data"
+private const val CLEAR_LOGCAT = "clear_logcat"
+private const val CLEAR_CACHE = "clear_cache"
+private const val HOMEPAGE = "homepage"
+private const val FONT_SIZE = "font_size"
+private const val OPEN_INTENTS_IN_NEW_TAB = "open_intents_in_new_tab"
+private const val SWIPE_TO_REFRESH = "swipe_to_refresh"
+private const val DOWNLOAD_WITH_EXTERNAL_APP = "download_with_external_app"
+private const val SCROLL_APP_BAR = "scroll_app_bar"
+private const val BOTTOM_APP_BAR = "bottom_app_bar"
+private const val DISPLAY_ADDITIONAL_APP_BAR_ICONS = "display_additional_app_bar_icons"
+private const val APP_THEME = "app_theme"
+private const val WEBVIEW_THEME = "webview_theme"
+private const val WIDE_VIEWPORT = "wide_viewport"
+private const val DISPLAY_WEBPAGE_IMAGES = "display_webpage_images"
+
+class ImportExportDatabaseHelper {
+    // Define the public companion object constants.  These can be moved to public class constants once the entire project has migrated to Kotlin.
+    companion object {
+        // Define the public class constants.
+        const val EXPORT_SUCCESSFUL = "Export Successful"
+        const val IMPORT_SUCCESSFUL = "Import Successful"
+    }
+
+    fun importUnencrypted(importFileInputStream: InputStream, context: Context): String {
+        return try {
+            // Create a temporary import file.
+            val temporaryImportFile = File.createTempFile("temporary_import_file", null, context.cacheDir)
+
+            // The file may be copied directly in Kotlin using `File.copyTo`.  <https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/java.io.-file/copy-to.html>
+            // It can be copied in Android using `Files.copy` once the minimum API >= 26.
+            // <https://developer.android.com/reference/java/nio/file/Files#copy(java.nio.file.Path,%20java.nio.file.Path,%20java.nio.file.CopyOption...)>
+            // However, the file cannot be acquired from the content URI until the minimum API >= 29.  <https://developer.android.com/reference/kotlin/android/content/ContentResolver#openfile>
+
+            // Create a temporary file output stream.
+            val temporaryImportFileOutputStream = FileOutputStream(temporaryImportFile)
+
+            // Create a transfer byte array.
+            val transferByteArray = ByteArray(1024)
+
+            // Create an integer to track the number of bytes read.
+            var bytesRead: Int
+
+            // Copy the import file to the temporary import file.
+            while (importFileInputStream.read(transferByteArray).also { bytesRead = it } > 0) {
+                temporaryImportFileOutputStream.write(transferByteArray, 0, bytesRead)
+            }
+
+            // Flush the temporary import file output stream.
+            temporaryImportFileOutputStream.flush()
+
+            // Close the file streams.
+            importFileInputStream.close()
+            temporaryImportFileOutputStream.close()
+
+
+            // Get a handle for the shared preference.
+            val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
+
+            // Open the import database.  Once the minimum API >= 27 the file can be opened directly without using the string.
+            val importDatabase = SQLiteDatabase.openDatabase(temporaryImportFile.toString(), null, SQLiteDatabase.OPEN_READWRITE)
+
+            // Get the database version.
+            val importDatabaseVersion = importDatabase.version
+
+            // Upgrade from schema version 1, first used in Privacy Browser 2.13, to schema version 2, first used in Privacy Browser 2.14.
+            // Previously this upgrade added `download_with_external_app` to the Preferences table.  But that is now removed in schema version 10.
+
+            // Upgrade from schema version 2, first used in Privacy Browser 2.14, to schema version 3, first used in Privacy Browser 2.15.
+            if (importDatabaseVersion < 3) {
+                // Once the SQLite version is >= 3.25.0 (API >= 30) `ALTER TABLE RENAME COLUMN` can be used.  <https://www.sqlite.org/lang_altertable.html> <https://www.sqlite.org/changes.html>
+                // <https://developer.android.com/reference/android/database/sqlite/package-summary>
+                // In the meantime, a new column must be created with the new name.  There is no need to delete the old column on the temporary import database.
+
+                // Create the new font size column.
+                importDatabase.execSQL("ALTER TABLE $PREFERENCES_TABLE ADD COLUMN $FONT_SIZE TEXT")
+
+                // Populate the preferences table with the current font size value.
+                importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $FONT_SIZE = default_font_size")
+            }
+
+            // Upgrade from schema version 3, first used in Privacy Browser 2.15, to schema version 4, first used in Privacy Browser 2.16.
+            if (importDatabaseVersion < 4) {
+                // Add the Pinned IP Addresses columns to the domains table.
+                importDatabase.execSQL("ALTER TABLE " + DomainsDatabaseHelper.DOMAINS_TABLE + " ADD COLUMN " + DomainsDatabaseHelper.PINNED_IP_ADDRESSES + " BOOLEAN")
+                importDatabase.execSQL("ALTER TABLE " + DomainsDatabaseHelper.DOMAINS_TABLE + " ADD COLUMN " + DomainsDatabaseHelper.IP_ADDRESSES + " TEXT")
+            }
+
+            // Upgrade from schema version 4, first used in Privacy Browser 2.16, to schema version 5, first used in Privacy Browser 2.17.
+            if (importDatabaseVersion < 5) {
+                // Add the hide and scroll app bar columns to the preferences table.
+                importDatabase.execSQL("ALTER TABLE $PREFERENCES_TABLE ADD COLUMN $HIDE_APP_BAR BOOLEAN")
+                importDatabase.execSQL("ALTER TABLE $PREFERENCES_TABLE ADD COLUMN $SCROLL_APP_BAR BOOLEAN")
+
+                // Get the current hide and scroll app bar settings.
+                val hideAppBar = sharedPreferences.getBoolean(HIDE_APP_BAR, true)
+                val scrollAppBar = sharedPreferences.getBoolean(SCROLL_APP_BAR, true)
+
+                // Populate the preferences table with the current app bar values.
+                // This can switch to using the variables directly once the API >= 30.  <https://www.sqlite.org/datatype3.html#boolean_datatype>
+                // <https://developer.android.com/reference/android/database/sqlite/package-summary>
+                if (hideAppBar) {
+                    importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $HIDE_APP_BAR = 1")
+                } else {
+                    importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $HIDE_APP_BAR = 0")
+                }
+
+                if (scrollAppBar) {
+                    importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $SCROLL_APP_BAR = 1")
+                } else {
+                    importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $SCROLL_APP_BAR = 0")
+                }
+            }
+
+            // Upgrade from schema version 5, first used in Privacy Browser 2.17, to schema version 6, first used in Privacy Browser 3.0.
+            if (importDatabaseVersion < 6) {
+                // Add the open intents in new tab column to the preferences table.
+                importDatabase.execSQL("ALTER TABLE $PREFERENCES_TABLE ADD COLUMN $OPEN_INTENTS_IN_NEW_TAB BOOLEAN")
+
+                // Get the current open intents in new tab preference.
+                val openIntentsInNewTab = sharedPreferences.getBoolean(OPEN_INTENTS_IN_NEW_TAB, true)
+
+                // Populate the preferences table with the current open intents value.
+                // This can switch to using the variables directly once the API >= 30.  <https://www.sqlite.org/datatype3.html#boolean_datatype>
+                // <https://developer.android.com/reference/android/database/sqlite/package-summary>
+                if (openIntentsInNewTab) {
+                    importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $OPEN_INTENTS_IN_NEW_TAB = 1")
+                } else {
+                    importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $OPEN_INTENTS_IN_NEW_TAB = 0")
+                }
+            }
+
+            // Upgrade from schema version 6, first used in Privacy Browser 3.0, to schema version 7, first used in Privacy Browser 3.1.
+            if (importDatabaseVersion < 7) {
+                // Add the wide viewport column to the domains table.
+                importDatabase.execSQL("ALTER TABLE " + DomainsDatabaseHelper.DOMAINS_TABLE + " ADD COLUMN " + DomainsDatabaseHelper.WIDE_VIEWPORT + " INTEGER")
+
+                // Add the Google Analytics, Facebook Click IDs, Twitter AMP redirects, and wide viewport columns to the preferences table.
+                importDatabase.execSQL("ALTER TABLE $PREFERENCES_TABLE ADD COLUMN $GOOGLE_ANALYTICS BOOLEAN")
+                importDatabase.execSQL("ALTER TABLE $PREFERENCES_TABLE ADD COLUMN $FACEBOOK_CLICK_IDS BOOLEAN")
+                importDatabase.execSQL("ALTER TABLE $PREFERENCES_TABLE ADD COLUMN $TWITTER_AMP_REDIRECTS BOOLEAN")
+                importDatabase.execSQL("ALTER TABLE $PREFERENCES_TABLE ADD COLUMN $WIDE_VIEWPORT BOOLEAN")
+
+                // Get the current preference values.
+                val googleAnalytics = sharedPreferences.getBoolean(GOOGLE_ANALYTICS, true)
+                val facebookClickIds = sharedPreferences.getBoolean(FACEBOOK_CLICK_IDS, true)
+                val twitterAmpRedirects = sharedPreferences.getBoolean(TWITTER_AMP_REDIRECTS, true)
+                val wideViewport = sharedPreferences.getBoolean(WIDE_VIEWPORT, true)
+
+                // Populate the preferences with the current Google Analytics value.
+                // This can switch to using the variables directly once the API >= 30.  <https://www.sqlite.org/datatype3.html#boolean_datatype>
+                // <https://developer.android.com/reference/android/database/sqlite/package-summary>
+                if (googleAnalytics) {
+                    importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $GOOGLE_ANALYTICS = 1")
+                } else {
+                    importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $GOOGLE_ANALYTICS = 0")
+                }
+
+                // Populate the preferences with the current Facebook Click IDs value.
+                if (facebookClickIds) {
+                    importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $FACEBOOK_CLICK_IDS = 1")
+                } else {
+                    importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $FACEBOOK_CLICK_IDS = 0")
+                }
+
+                // Populate the preferences table with the current Twitter AMP redirects value.
+                if (twitterAmpRedirects) {
+                    importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $TWITTER_AMP_REDIRECTS = 1")
+                } else {
+                    importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $TWITTER_AMP_REDIRECTS = 0")
+                }
+
+                // Populate the preferences table with the current wide viewport value.
+                if (wideViewport) {
+                    importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $WIDE_VIEWPORT = 1")
+                } else {
+                    importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $WIDE_VIEWPORT = 0")
+                }
+            }
+
+            // Upgrade from schema version 7, first used in Privacy Browser 3.1, to schema version 8, first used in Privacy Browser 3.2.
+            if (importDatabaseVersion < 8) {
+                // Add the UltraList column to the tables.
+                importDatabase.execSQL("ALTER TABLE " + DomainsDatabaseHelper.DOMAINS_TABLE + " ADD COLUMN " + DomainsDatabaseHelper.ULTRALIST + " BOOLEAN")
+                importDatabase.execSQL("ALTER TABLE $PREFERENCES_TABLE ADD COLUMN $ULTRALIST BOOLEAN")
+
+                // Get the current preference values.
+                val ultraList = sharedPreferences.getBoolean(ULTRALIST, true)
+
+                // Populate the tables with the current UltraList value.
+                // This can switch to using the variables directly once the API >= 30.  <https://www.sqlite.org/datatype3.html#boolean_datatype>
+                // <https://developer.android.com/reference/android/database/sqlite/package-summary>
+                if (ultraList) {
+                    importDatabase.execSQL("UPDATE " + DomainsDatabaseHelper.DOMAINS_TABLE + " SET " + DomainsDatabaseHelper.ULTRALIST + " = " + 1)
+                    importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $ULTRALIST = 1")
+                } else {
+                    importDatabase.execSQL("UPDATE " + DomainsDatabaseHelper.DOMAINS_TABLE + " SET " + DomainsDatabaseHelper.ULTRALIST + " = " + 0)
+                    importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $ULTRALIST = 0")
+                }
+            }
+
+            // Upgrade from schema version 8, first used in Privacy Browser 3.2, to schema version 9, first used in Privacy Browser 3.3.
+            if (importDatabaseVersion < 9) {
+                // Add the new proxy columns to the preferences table.
+                importDatabase.execSQL("ALTER TABLE $PREFERENCES_TABLE ADD COLUMN $PROXY TEXT")
+                importDatabase.execSQL("ALTER TABLE $PREFERENCES_TABLE ADD COLUMN $PROXY_CUSTOM_URL TEXT")
+
+                // Get the current proxy values.
+                val proxy = sharedPreferences.getString(PROXY, context.getString(R.string.proxy_default_value))
+                var proxyCustomUrl = sharedPreferences.getString(PROXY_CUSTOM_URL, context.getString(R.string.proxy_custom_url_default_value))
+
+                // SQL escape the proxy custom URL string.
+                proxyCustomUrl = DatabaseUtils.sqlEscapeString(proxyCustomUrl)
+
+                // Populate the preferences table with the current proxy values. The proxy custom URL does not need to be surrounded by `'` because it was SLQ escaped above.
+                importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $PROXY = '$proxy'")
+                importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $PROXY_CUSTOM_URL = $proxyCustomUrl")
+            }
+
+            // Upgrade from schema version 9, first used in Privacy Browser 3.3, to schema version 10, first used in Privacy Browser 3.4.
+            // Previously this upgrade added `download_location` and `download_custom_location` to the Preferences table.  But they were removed in schema version 13.
+
+            // Upgrade from schema version 10, first used in Privacy Browser 3.4, to schema version 11, first used in Privacy Browser 3.5.
+            if (importDatabaseVersion < 11) {
+                // Add the app theme column to the preferences table.
+                importDatabase.execSQL("ALTER TABLE $PREFERENCES_TABLE ADD COLUMN $APP_THEME TEXT")
+
+                // Get a cursor for the dark theme preference.
+                val darkThemePreferencesCursor = importDatabase.rawQuery("SELECT dark_theme FROM $PREFERENCES_TABLE", null)
+
+                // Move to the first entry.
+                darkThemePreferencesCursor.moveToFirst()
+
+                // Get the old dark theme value, which is in column 0.
+                val darkTheme = darkThemePreferencesCursor.getInt(0)
+
+                // Close the dark theme preference cursor.
+                darkThemePreferencesCursor.close()
+
+                // Get the system default string.
+                val systemDefault = context.getString(R.string.app_theme_default_value)
+
+                // Get the theme entry values string array.
+                val appThemeEntryValuesStringArray: Array<String> = context.resources.getStringArray(R.array.app_theme_entry_values)
+
+                // Get the dark string.
+                val dark = appThemeEntryValuesStringArray[2]
+
+                // Populate the app theme according to the old dark theme preference.
+                if (darkTheme == 0) {  // A light theme was selected.
+                    // Set the app theme to be the system default.
+                    importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $APP_THEME = '$systemDefault'")
+                } else {  // A dark theme was selected.
+                    // Set the app theme to be dark.
+                    importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $APP_THEME = '$dark'")
+                }
+
+                // Add the WebView theme to the domains table.  This defaults to 0, which is `System default`, so a separate step isn't needed to populate the database.
+                importDatabase.execSQL("ALTER TABLE " + DomainsDatabaseHelper.DOMAINS_TABLE + " ADD COLUMN " + DomainsDatabaseHelper.WEBVIEW_THEME + " INTEGER")
+
+                // Add the WebView theme to the preferences table.
+                importDatabase.execSQL("ALTER TABLE $PREFERENCES_TABLE ADD COLUMN $WEBVIEW_THEME TEXT")
+
+                // Get the WebView theme default value string.
+                val webViewThemeDefaultValue = context.getString(R.string.webview_theme_default_value)
+
+                // Set the WebView theme in the preferences table to be the default.
+                importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $WEBVIEW_THEME = '$webViewThemeDefaultValue'")
+            }
+
+            // Upgrade from schema version 11, first used in Privacy Browser 3.5, to schema version 12, first used in Privacy Browser 3.6.
+            if (importDatabaseVersion < 12) {
+                // Add the clear logcat column to the preferences table.
+                importDatabase.execSQL("ALTER TABLE $PREFERENCES_TABLE ADD COLUMN $CLEAR_LOGCAT BOOLEAN")
+
+                // Get the current clear logcat value.
+                val clearLogcat = sharedPreferences.getBoolean(CLEAR_LOGCAT, true)
+
+                // Populate the preferences table with the current clear logcat value.
+                // This can switch to using the variables directly once the API >= 30.  <https://www.sqlite.org/datatype3.html#boolean_datatype>
+                // <https://developer.android.com/reference/android/database/sqlite/package-summary>
+                if (clearLogcat) {
+                    importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $CLEAR_LOGCAT = 1")
+                } else {
+                    importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $CLEAR_LOGCAT = 0")
+                }
+            }
+
+            // Upgrade from schema version 12, first used in Privacy Browser 3.6, to schema version 13, first used in Privacy Browser 3.7.
+            // Do nothing.  `download_location` and `download_custom_location` were removed from the preferences table, but they can be left in the temporary import database without issue.
+
+            // Upgrade from schema version 13, first used in Privacy Browser 3.7, to schema version 14, first used in Privacy Browser 3.8.
+            if (importDatabaseVersion < 14) {
+                // `enabledthirdpartycookies` was removed from the domains table.  `do_not_track` and `third_party_cookies` were removed from the preferences table.
+
+                // Once the SQLite version is >= 3.25.0 `ALTER TABLE RENAME COLUMN` can be used.  <https://www.sqlite.org/lang_altertable.html> <https://www.sqlite.org/changes.html>
+                // <https://developer.android.com/reference/android/database/sqlite/package-summary>
+                // In the meantime, a new column must be created with the new name.  There is no need to delete the old column on the temporary import database.
+
+                // Create the new cookies columns.
+                importDatabase.execSQL("ALTER TABLE " + DomainsDatabaseHelper.DOMAINS_TABLE + " ADD COLUMN " + DomainsDatabaseHelper.COOKIES + " BOOLEAN")
+                importDatabase.execSQL("ALTER TABLE $PREFERENCES_TABLE ADD COLUMN $COOKIES BOOLEAN")
+
+                // Copy the data from the old cookies columns to the new ones.
+                importDatabase.execSQL("UPDATE " + DomainsDatabaseHelper.DOMAINS_TABLE + " SET " + DomainsDatabaseHelper.COOKIES + " = enablefirstpartycookies")
+                importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $COOKIES = first_party_cookies")
+
+                // Create the new download with external app and bottom app bar columns.
+                importDatabase.execSQL("ALTER TABLE $PREFERENCES_TABLE ADD COLUMN $DOWNLOAD_WITH_EXTERNAL_APP BOOLEAN")
+                importDatabase.execSQL("ALTER TABLE $PREFERENCES_TABLE ADD COLUMN $BOTTOM_APP_BAR BOOLEAN")
+
+                // Get the current values for the new columns.
+                val downloadWithExternalApp = sharedPreferences.getBoolean(DOWNLOAD_WITH_EXTERNAL_APP, false)
+                val bottomAppBar = sharedPreferences.getBoolean(BOTTOM_APP_BAR, false)
+
+                // Populate the preferences table with the current download with external app value.
+                // This can switch to using the variables directly once the API >= 30.  <https://www.sqlite.org/datatype3.html#boolean_datatype>
+                // <https://developer.android.com/reference/android/database/sqlite/package-summary>
+                if (downloadWithExternalApp) {
+                    importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $DOWNLOAD_WITH_EXTERNAL_APP = 1")
+                } else {
+                    importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $DOWNLOAD_WITH_EXTERNAL_APP = 0")
+                }
+
+                // Populate the preferences table with the current bottom app bar value.
+                if (bottomAppBar) {
+                    importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $BOTTOM_APP_BAR = 1")
+                } else {
+                    importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $BOTTOM_APP_BAR = 0")
+                }
+            }
+
+            // Get a cursor for the bookmarks table.
+            val importBookmarksCursor = importDatabase.rawQuery("SELECT * FROM " + BookmarksDatabaseHelper.BOOKMARKS_TABLE, null)
+
+            // Delete the current bookmarks database.
+            context.deleteDatabase(BookmarksDatabaseHelper.BOOKMARKS_DATABASE)
+
+            // Create a new bookmarks database.
+            val bookmarksDatabaseHelper = BookmarksDatabaseHelper(context)
+
+            // Move to the first record.
+            importBookmarksCursor.moveToFirst()
+
+            // Copy the data from the import bookmarks cursor into the bookmarks database.
+            for (i in 0 until importBookmarksCursor.count) {
+                // Create a bookmark content values.
+                val bookmarkContentValues = ContentValues()
+
+                // Add the information for this bookmark to the content values.
+                bookmarkContentValues.put(BookmarksDatabaseHelper.BOOKMARK_NAME, importBookmarksCursor.getString(importBookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_NAME)))
+                bookmarkContentValues.put(BookmarksDatabaseHelper.BOOKMARK_URL, importBookmarksCursor.getString(importBookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_URL)))
+                bookmarkContentValues.put(BookmarksDatabaseHelper.PARENT_FOLDER, importBookmarksCursor.getString(importBookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.PARENT_FOLDER)))
+                bookmarkContentValues.put(BookmarksDatabaseHelper.DISPLAY_ORDER, importBookmarksCursor.getInt(importBookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.DISPLAY_ORDER)))
+                bookmarkContentValues.put(BookmarksDatabaseHelper.IS_FOLDER, importBookmarksCursor.getInt(importBookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.IS_FOLDER)))
+                bookmarkContentValues.put(BookmarksDatabaseHelper.FAVORITE_ICON, importBookmarksCursor.getBlob(importBookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.FAVORITE_ICON)))
+
+                // Insert the content values into the bookmarks database.
+                bookmarksDatabaseHelper.createBookmark(bookmarkContentValues)
+
+                // Advance to the next record.
+                importBookmarksCursor.moveToNext()
+            }
+
+            // Close the bookmarks cursor and database.
+            importBookmarksCursor.close()
+            bookmarksDatabaseHelper.close()
+
+
+            // Get a cursor for the domains table.
+            val importDomainsCursor = importDatabase.rawQuery("SELECT * FROM " + DomainsDatabaseHelper.DOMAINS_TABLE + " ORDER BY " + DomainsDatabaseHelper.DOMAIN_NAME + " ASC", null)
+
+            // Delete the current domains database.
+            context.deleteDatabase(DomainsDatabaseHelper.DOMAINS_DATABASE)
+
+            // Create a new domains database.
+            val domainsDatabaseHelper = DomainsDatabaseHelper(context)
+
+            // Move to the first record.
+            importDomainsCursor.moveToFirst()
+
+            // Copy the data from the import domains cursor into the domains database.
+            for (i in 0 until importDomainsCursor.count) {
+                // Create a domain content values.
+                val domainContentValues = ContentValues()
+
+                // Populate the domain content values.
+                domainContentValues.put(DomainsDatabaseHelper.DOMAIN_NAME, importDomainsCursor.getString(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DOMAIN_NAME)))
+                domainContentValues.put(DomainsDatabaseHelper.ENABLE_JAVASCRIPT, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_JAVASCRIPT)))
+                domainContentValues.put(DomainsDatabaseHelper.COOKIES, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.COOKIES)))
+                domainContentValues.put(DomainsDatabaseHelper.ENABLE_DOM_STORAGE, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_DOM_STORAGE)))
+                domainContentValues.put(DomainsDatabaseHelper.ENABLE_FORM_DATA, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FORM_DATA)))
+                domainContentValues.put(DomainsDatabaseHelper.ENABLE_EASYLIST, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_EASYLIST)))
+                domainContentValues.put(DomainsDatabaseHelper.ENABLE_EASYPRIVACY, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_EASYPRIVACY)))
+                domainContentValues.put(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST,
+                    importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST)))
+                domainContentValues.put(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST,
+                    importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST)))
+                domainContentValues.put(DomainsDatabaseHelper.ULTRALIST, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ULTRALIST)))
+                domainContentValues.put(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY)))
+                domainContentValues.put(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS,
+                    importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS)))
+                domainContentValues.put(DomainsDatabaseHelper.USER_AGENT, importDomainsCursor.getString(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.USER_AGENT)))
+                domainContentValues.put(DomainsDatabaseHelper.FONT_SIZE, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.FONT_SIZE)))
+                domainContentValues.put(DomainsDatabaseHelper.SWIPE_TO_REFRESH, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SWIPE_TO_REFRESH)))
+                domainContentValues.put(DomainsDatabaseHelper.WEBVIEW_THEME, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.WEBVIEW_THEME)))
+                domainContentValues.put(DomainsDatabaseHelper.WIDE_VIEWPORT, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.WIDE_VIEWPORT)))
+                domainContentValues.put(DomainsDatabaseHelper.DISPLAY_IMAGES, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DISPLAY_IMAGES)))
+                domainContentValues.put(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE)))
+                domainContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME,
+                    importDomainsCursor.getString(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME)))
+                domainContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION,
+                    importDomainsCursor.getString(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION)))
+                domainContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT,
+                    importDomainsCursor.getString(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT)))
+                domainContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME,
+                    importDomainsCursor.getString(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME)))
+                domainContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION,
+                    importDomainsCursor.getString(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION)))
+                domainContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT,
+                    importDomainsCursor.getString(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT)))
+                domainContentValues.put(DomainsDatabaseHelper.SSL_START_DATE, importDomainsCursor.getLong(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_START_DATE)))
+                domainContentValues.put(DomainsDatabaseHelper.SSL_END_DATE, importDomainsCursor.getLong(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_END_DATE)))
+                domainContentValues.put(DomainsDatabaseHelper.PINNED_IP_ADDRESSES, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.PINNED_IP_ADDRESSES)))
+                domainContentValues.put(DomainsDatabaseHelper.IP_ADDRESSES, importDomainsCursor.getString(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.IP_ADDRESSES)))
+
+                // Insert the content values into the domains database.
+                domainsDatabaseHelper.addDomain(domainContentValues)
+
+                // Advance to the next record.
+                importDomainsCursor.moveToNext()
+            }
+
+            // Close the domains cursor and database.
+            importDomainsCursor.close()
+            domainsDatabaseHelper.close()
+
+
+            // Get a cursor for the preferences table.
+            val importPreferencesCursor = importDatabase.rawQuery("SELECT * FROM $PREFERENCES_TABLE", null)
+
+            // Move to the first record.
+            importPreferencesCursor.moveToFirst()
+
+            // Import the preference data.
+            sharedPreferences.edit()
+                .putBoolean(JAVASCRIPT, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(JAVASCRIPT)) == 1)
+                .putBoolean(COOKIES, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(COOKIES)) == 1)
+                .putBoolean(DOM_STORAGE, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(DOM_STORAGE)) == 1)
+                .putBoolean(SAVE_FORM_DATA, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(SAVE_FORM_DATA)) == 1)  // Save form data can be removed once the minimum API >= 26.
+                .putString(USER_AGENT, importPreferencesCursor.getString(importPreferencesCursor.getColumnIndexOrThrow(USER_AGENT)))
+                .putString(CUSTOM_USER_AGENT, importPreferencesCursor.getString(importPreferencesCursor.getColumnIndexOrThrow(CUSTOM_USER_AGENT)))
+                .putBoolean(INCOGNITO_MODE, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(INCOGNITO_MODE)) == 1)
+                .putBoolean(ALLOW_SCREENSHOTS, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(ALLOW_SCREENSHOTS)) == 1)
+                .putBoolean(EASYLIST, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(EASYLIST)) == 1)
+                .putBoolean(EASYPRIVACY, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(EASYPRIVACY)) == 1)
+                .putBoolean(FANBOYS_ANNOYANCE_LIST, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(FANBOYS_ANNOYANCE_LIST)) == 1)
+                .putBoolean(FANBOYS_SOCIAL_BLOCKING_LIST, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(FANBOYS_SOCIAL_BLOCKING_LIST)) == 1)
+                .putBoolean(ULTRALIST, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(ULTRALIST)) == 1)
+                .putBoolean(ULTRAPRIVACY, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(ULTRAPRIVACY)) == 1)
+                .putBoolean(BLOCK_ALL_THIRD_PARTY_REQUESTS, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(BLOCK_ALL_THIRD_PARTY_REQUESTS)) == 1)
+                .putBoolean(GOOGLE_ANALYTICS, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(GOOGLE_ANALYTICS)) == 1)
+                .putBoolean(FACEBOOK_CLICK_IDS, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(FACEBOOK_CLICK_IDS)) == 1)
+                .putBoolean(TWITTER_AMP_REDIRECTS, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(TWITTER_AMP_REDIRECTS)) == 1)
+                .putString(SEARCH, importPreferencesCursor.getString(importPreferencesCursor.getColumnIndexOrThrow(SEARCH)))
+                .putString(SEARCH_CUSTOM_URL, importPreferencesCursor.getString(importPreferencesCursor.getColumnIndexOrThrow(SEARCH_CUSTOM_URL)))
+                .putString(PROXY, importPreferencesCursor.getString(importPreferencesCursor.getColumnIndexOrThrow(PROXY)))
+                .putString(PROXY_CUSTOM_URL, importPreferencesCursor.getString(importPreferencesCursor.getColumnIndexOrThrow(PROXY_CUSTOM_URL)))
+                .putBoolean(FULL_SCREEN_BROWSING_MODE, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(FULL_SCREEN_BROWSING_MODE)) == 1)
+                .putBoolean(HIDE_APP_BAR, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(HIDE_APP_BAR)) == 1)
+                .putBoolean(CLEAR_EVERYTHING, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(CLEAR_EVERYTHING)) == 1)
+                .putBoolean(CLEAR_COOKIES, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(CLEAR_COOKIES)) == 1)
+                .putBoolean(CLEAR_DOM_STORAGE, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(CLEAR_DOM_STORAGE)) == 1)
+                .putBoolean(CLEAR_FORM_DATA, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(CLEAR_FORM_DATA)) == 1)  // Clear form data can be removed once the minimum API >= 26.
+                .putBoolean(CLEAR_LOGCAT, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(CLEAR_LOGCAT)) == 1)
+                .putBoolean(CLEAR_CACHE, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(CLEAR_CACHE)) == 1)
+                .putString(HOMEPAGE, importPreferencesCursor.getString(importPreferencesCursor.getColumnIndexOrThrow(HOMEPAGE)))
+                .putString(FONT_SIZE, importPreferencesCursor.getString(importPreferencesCursor.getColumnIndexOrThrow(FONT_SIZE)))
+                .putBoolean(OPEN_INTENTS_IN_NEW_TAB, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(OPEN_INTENTS_IN_NEW_TAB)) == 1)
+                .putBoolean(SWIPE_TO_REFRESH, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(SWIPE_TO_REFRESH)) == 1)
+                .putBoolean(DOWNLOAD_WITH_EXTERNAL_APP, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(DOWNLOAD_WITH_EXTERNAL_APP)) == 1)
+                .putBoolean(SCROLL_APP_BAR, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(SCROLL_APP_BAR)) == 1)
+                .putBoolean(BOTTOM_APP_BAR, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(BOTTOM_APP_BAR)) == 1)
+                .putBoolean(DISPLAY_ADDITIONAL_APP_BAR_ICONS, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(DISPLAY_ADDITIONAL_APP_BAR_ICONS)) == 1)
+                .putString(APP_THEME, importPreferencesCursor.getString(importPreferencesCursor.getColumnIndexOrThrow(APP_THEME)))
+                .putString(WEBVIEW_THEME, importPreferencesCursor.getString(importPreferencesCursor.getColumnIndexOrThrow(WEBVIEW_THEME)))
+                .putBoolean(WIDE_VIEWPORT, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(WIDE_VIEWPORT)) == 1)
+                .putBoolean(DISPLAY_WEBPAGE_IMAGES, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(DISPLAY_WEBPAGE_IMAGES)) == 1)
+                .apply()
+
+            // Close the preferences cursor and database.
+            importPreferencesCursor.close()
+            importDatabase.close()
+
+            // Delete the temporary import file database, journal, and other related auxiliary files.
+            SQLiteDatabase.deleteDatabase(temporaryImportFile)
+
+            // Return the import successful string.
+            IMPORT_SUCCESSFUL
+        } catch (exception: Exception) {
+            // Return the import error.
+            exception.toString()
+        }
+    }
+
+    fun exportUnencrypted(exportFileOutputStream: OutputStream, context: Context): String {
+        return try {
+            // Create a temporary export file.
+            val temporaryExportFile = File.createTempFile("temporary_export_file", null, context.cacheDir)
+
+            // Create the temporary export database.
+            val temporaryExportDatabase = SQLiteDatabase.openOrCreateDatabase(temporaryExportFile, null)
+
+            // Set the temporary export database version number.
+            temporaryExportDatabase.version = SCHEMA_VERSION
+
+
+            // Create the temporary export database bookmarks table.
+            temporaryExportDatabase.execSQL(BookmarksDatabaseHelper.CREATE_BOOKMARKS_TABLE)
+
+            // Open the bookmarks database.
+            val bookmarksDatabaseHelper = BookmarksDatabaseHelper(context)
+
+            // Get a full bookmarks cursor.
+            val bookmarksCursor = bookmarksDatabaseHelper.allBookmarks
+
+            // Move to the first record.
+            bookmarksCursor.moveToFirst()
+
+            // Copy the data from the bookmarks cursor into the export database.
+            for (i in 0 until bookmarksCursor.count) {
+                // Create a bookmark content values.
+                val bookmarkContentValues = ContentValues()
+
+                // Populate the bookmark content values.
+                bookmarkContentValues.put(BookmarksDatabaseHelper.BOOKMARK_NAME, bookmarksCursor.getString(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_NAME)))
+                bookmarkContentValues.put(BookmarksDatabaseHelper.BOOKMARK_URL, bookmarksCursor.getString(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_URL)))
+                bookmarkContentValues.put(BookmarksDatabaseHelper.PARENT_FOLDER, bookmarksCursor.getString(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.PARENT_FOLDER)))
+                bookmarkContentValues.put(BookmarksDatabaseHelper.DISPLAY_ORDER, bookmarksCursor.getInt(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.DISPLAY_ORDER)))
+                bookmarkContentValues.put(BookmarksDatabaseHelper.IS_FOLDER, bookmarksCursor.getInt(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.IS_FOLDER)))
+                bookmarkContentValues.put(BookmarksDatabaseHelper.FAVORITE_ICON, bookmarksCursor.getBlob(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.FAVORITE_ICON)))
+
+                // Insert the content values into the temporary export database.
+                temporaryExportDatabase.insert(BookmarksDatabaseHelper.BOOKMARKS_TABLE, null, bookmarkContentValues)
+
+                // Advance to the next record.
+                bookmarksCursor.moveToNext()
+            }
+
+            // Close the bookmarks cursor and database.
+            bookmarksCursor.close()
+            bookmarksDatabaseHelper.close()
+
+
+            // Create the temporary export database domains table.
+            temporaryExportDatabase.execSQL(DomainsDatabaseHelper.CREATE_DOMAINS_TABLE)
+
+            // Open the domains database.
+            val domainsDatabaseHelper = DomainsDatabaseHelper(context)
+
+            // Get a full domains database cursor.
+            val domainsCursor = domainsDatabaseHelper.completeCursorOrderedByDomain
+
+            // Move to the first record.
+            domainsCursor.moveToFirst()
+
+            // Copy the data from the domains cursor into the export database.
+            for (i in 0 until domainsCursor.count) {
+                // Create a domain content values.
+                val domainContentValues = ContentValues()
+
+                // Populate the domain content values.
+                domainContentValues.put(DomainsDatabaseHelper.DOMAIN_NAME, domainsCursor.getString(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DOMAIN_NAME)))
+                domainContentValues.put(DomainsDatabaseHelper.ENABLE_JAVASCRIPT, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_JAVASCRIPT)))
+                domainContentValues.put(DomainsDatabaseHelper.COOKIES, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.COOKIES)))
+                domainContentValues.put(DomainsDatabaseHelper.ENABLE_DOM_STORAGE, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_DOM_STORAGE)))
+                domainContentValues.put(DomainsDatabaseHelper.ENABLE_FORM_DATA, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FORM_DATA)))
+                domainContentValues.put(DomainsDatabaseHelper.ENABLE_EASYLIST, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_EASYLIST)))
+                domainContentValues.put(DomainsDatabaseHelper.ENABLE_EASYPRIVACY, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_EASYPRIVACY)))
+                domainContentValues.put(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST)))
+                domainContentValues.put(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST,
+                    domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST)))
+                domainContentValues.put(DomainsDatabaseHelper.ULTRALIST, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ULTRALIST)))
+                domainContentValues.put(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY)))
+                domainContentValues.put(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS)))
+                domainContentValues.put(DomainsDatabaseHelper.USER_AGENT, domainsCursor.getString(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.USER_AGENT)))
+                domainContentValues.put(DomainsDatabaseHelper.FONT_SIZE, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.FONT_SIZE)))
+                domainContentValues.put(DomainsDatabaseHelper.SWIPE_TO_REFRESH, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SWIPE_TO_REFRESH)))
+                domainContentValues.put(DomainsDatabaseHelper.WEBVIEW_THEME, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.WEBVIEW_THEME)))
+                domainContentValues.put(DomainsDatabaseHelper.WIDE_VIEWPORT, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.WIDE_VIEWPORT)))
+                domainContentValues.put(DomainsDatabaseHelper.DISPLAY_IMAGES, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DISPLAY_IMAGES)))
+                domainContentValues.put(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE)))
+                domainContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME, domainsCursor.getString(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME)))
+                domainContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION, domainsCursor.getString(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION)))
+                domainContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT,
+                    domainsCursor.getString(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT)))
+                domainContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME, domainsCursor.getString(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME)))
+                domainContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION, domainsCursor.getString(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION)))
+                domainContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT,
+                    domainsCursor.getString(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT)))
+                domainContentValues.put(DomainsDatabaseHelper.SSL_START_DATE, domainsCursor.getLong(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_START_DATE)))
+                domainContentValues.put(DomainsDatabaseHelper.SSL_END_DATE, domainsCursor.getLong(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_END_DATE)))
+                domainContentValues.put(DomainsDatabaseHelper.PINNED_IP_ADDRESSES, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.PINNED_IP_ADDRESSES)))
+                domainContentValues.put(DomainsDatabaseHelper.IP_ADDRESSES, domainsCursor.getString(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.IP_ADDRESSES)))
+
+                // Insert the content values into the temporary export database.
+                temporaryExportDatabase.insert(DomainsDatabaseHelper.DOMAINS_TABLE, null, domainContentValues)
+
+                // Advance to the next record.
+                domainsCursor.moveToNext()
+            }
+
+            // Close the domains cursor and database.
+            domainsCursor.close()
+            domainsDatabaseHelper.close()
+
+
+            // Prepare the preferences table SQL creation string.
+            val createPreferencesTable = "CREATE TABLE " + PREFERENCES_TABLE + " (" +
+                    ID + " INTEGER PRIMARY KEY, " +
+                    JAVASCRIPT + " BOOLEAN, " +
+                    COOKIES + " BOOLEAN, " +
+                    DOM_STORAGE + " BOOLEAN, " +
+                    SAVE_FORM_DATA + " BOOLEAN, " +
+                    USER_AGENT + " TEXT, " +
+                    CUSTOM_USER_AGENT + " TEXT, " +
+                    INCOGNITO_MODE + " BOOLEAN, " +
+                    ALLOW_SCREENSHOTS + " BOOLEAN, " +
+                    EASYLIST + " BOOLEAN, " +
+                    EASYPRIVACY + " BOOLEAN, " +
+                    FANBOYS_ANNOYANCE_LIST + " BOOLEAN, " +
+                    FANBOYS_SOCIAL_BLOCKING_LIST + " BOOLEAN, " +
+                    ULTRALIST + " BOOLEAN, " +
+                    ULTRAPRIVACY + " BOOLEAN, " +
+                    BLOCK_ALL_THIRD_PARTY_REQUESTS + " BOOLEAN, " +
+                    GOOGLE_ANALYTICS + " BOOLEAN, " +
+                    FACEBOOK_CLICK_IDS + " BOOLEAN, " +
+                    TWITTER_AMP_REDIRECTS + " BOOLEAN, " +
+                    SEARCH + " TEXT, " +
+                    SEARCH_CUSTOM_URL + " TEXT, " +
+                    PROXY + " TEXT, " +
+                    PROXY_CUSTOM_URL + " TEXT, " +
+                    FULL_SCREEN_BROWSING_MODE + " BOOLEAN, " +
+                    HIDE_APP_BAR + " BOOLEAN, " +
+                    CLEAR_EVERYTHING + " BOOLEAN, " +
+                    CLEAR_COOKIES + " BOOLEAN, " +
+                    CLEAR_DOM_STORAGE + " BOOLEAN, " +
+                    CLEAR_FORM_DATA + " BOOLEAN, " +
+                    CLEAR_LOGCAT + " BOOLEAN, " +
+                    CLEAR_CACHE + " BOOLEAN, " +
+                    HOMEPAGE + " TEXT, " +
+                    FONT_SIZE + " TEXT, " +
+                    OPEN_INTENTS_IN_NEW_TAB + " BOOLEAN, " +
+                    SWIPE_TO_REFRESH + " BOOLEAN, " +
+                    DOWNLOAD_WITH_EXTERNAL_APP + " BOOLEAN, " +
+                    SCROLL_APP_BAR + " BOOLEAN, " +
+                    BOTTOM_APP_BAR + " BOOLEAN, " +
+                    DISPLAY_ADDITIONAL_APP_BAR_ICONS + " BOOLEAN, " +
+                    APP_THEME + " TEXT, " +
+                    WEBVIEW_THEME + " TEXT, " +
+                    WIDE_VIEWPORT + " BOOLEAN, " +
+                    DISPLAY_WEBPAGE_IMAGES + " BOOLEAN)"
+
+            // Create the temporary export database preferences table.
+            temporaryExportDatabase.execSQL(createPreferencesTable)
+
+            // Get a handle for the shared preference.
+            val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
+
+            // Create a preferences content values.
+            val preferencesContentValues = ContentValues()
+
+            // Populate the preferences content values.
+            preferencesContentValues.put(JAVASCRIPT, sharedPreferences.getBoolean(JAVASCRIPT, false))
+            preferencesContentValues.put(COOKIES, sharedPreferences.getBoolean(COOKIES, false))
+            preferencesContentValues.put(DOM_STORAGE, sharedPreferences.getBoolean(DOM_STORAGE, false))
+            preferencesContentValues.put(SAVE_FORM_DATA, sharedPreferences.getBoolean(SAVE_FORM_DATA, false))  // Save form data can be removed once the minimum API >= 26.
+            preferencesContentValues.put(USER_AGENT, sharedPreferences.getString(USER_AGENT, context.getString(R.string.user_agent_default_value)))
+            preferencesContentValues.put(CUSTOM_USER_AGENT, sharedPreferences.getString(CUSTOM_USER_AGENT, context.getString(R.string.custom_user_agent_default_value)))
+            preferencesContentValues.put(INCOGNITO_MODE, sharedPreferences.getBoolean(INCOGNITO_MODE, false))
+            preferencesContentValues.put(ALLOW_SCREENSHOTS, sharedPreferences.getBoolean(ALLOW_SCREENSHOTS, false))
+            preferencesContentValues.put(EASYLIST, sharedPreferences.getBoolean(EASYLIST, true))
+            preferencesContentValues.put(EASYPRIVACY, sharedPreferences.getBoolean(EASYPRIVACY, true))
+            preferencesContentValues.put(FANBOYS_ANNOYANCE_LIST, sharedPreferences.getBoolean(FANBOYS_ANNOYANCE_LIST, true))
+            preferencesContentValues.put(FANBOYS_SOCIAL_BLOCKING_LIST, sharedPreferences.getBoolean(FANBOYS_SOCIAL_BLOCKING_LIST, true))
+            preferencesContentValues.put(ULTRALIST, sharedPreferences.getBoolean(ULTRALIST, true))
+            preferencesContentValues.put(ULTRAPRIVACY, sharedPreferences.getBoolean(ULTRAPRIVACY, true))
+            preferencesContentValues.put(BLOCK_ALL_THIRD_PARTY_REQUESTS, sharedPreferences.getBoolean(BLOCK_ALL_THIRD_PARTY_REQUESTS, false))
+            preferencesContentValues.put(GOOGLE_ANALYTICS, sharedPreferences.getBoolean(GOOGLE_ANALYTICS, true))
+            preferencesContentValues.put(FACEBOOK_CLICK_IDS, sharedPreferences.getBoolean(FACEBOOK_CLICK_IDS, true))
+            preferencesContentValues.put(TWITTER_AMP_REDIRECTS, sharedPreferences.getBoolean(TWITTER_AMP_REDIRECTS, true))
+            preferencesContentValues.put(SEARCH, sharedPreferences.getString(SEARCH, context.getString(R.string.search_default_value)))
+            preferencesContentValues.put(SEARCH_CUSTOM_URL, sharedPreferences.getString(SEARCH_CUSTOM_URL, context.getString(R.string.search_custom_url_default_value)))
+            preferencesContentValues.put(PROXY, sharedPreferences.getString(PROXY, context.getString(R.string.proxy_default_value)))
+            preferencesContentValues.put(PROXY_CUSTOM_URL, sharedPreferences.getString(PROXY_CUSTOM_URL, context.getString(R.string.proxy_custom_url_default_value)))
+            preferencesContentValues.put(FULL_SCREEN_BROWSING_MODE, sharedPreferences.getBoolean(FULL_SCREEN_BROWSING_MODE, false))
+            preferencesContentValues.put(HIDE_APP_BAR, sharedPreferences.getBoolean(HIDE_APP_BAR, true))
+            preferencesContentValues.put(CLEAR_EVERYTHING, sharedPreferences.getBoolean(CLEAR_EVERYTHING, true))
+            preferencesContentValues.put(CLEAR_COOKIES, sharedPreferences.getBoolean(CLEAR_COOKIES, true))
+            preferencesContentValues.put(CLEAR_DOM_STORAGE, sharedPreferences.getBoolean(CLEAR_DOM_STORAGE, true))
+            preferencesContentValues.put(CLEAR_FORM_DATA, sharedPreferences.getBoolean(CLEAR_FORM_DATA, true))  // Clear form data can be removed once the minimum API >= 26.
+            preferencesContentValues.put(CLEAR_LOGCAT, sharedPreferences.getBoolean(CLEAR_LOGCAT, true))
+            preferencesContentValues.put(CLEAR_CACHE, sharedPreferences.getBoolean(CLEAR_CACHE, true))
+            preferencesContentValues.put(HOMEPAGE, sharedPreferences.getString(HOMEPAGE, context.getString(R.string.homepage_default_value)))
+            preferencesContentValues.put(FONT_SIZE, sharedPreferences.getString(FONT_SIZE, context.getString(R.string.font_size_default_value)))
+            preferencesContentValues.put(OPEN_INTENTS_IN_NEW_TAB, sharedPreferences.getBoolean(OPEN_INTENTS_IN_NEW_TAB, true))
+            preferencesContentValues.put(SWIPE_TO_REFRESH, sharedPreferences.getBoolean(SWIPE_TO_REFRESH, true))
+            preferencesContentValues.put(DOWNLOAD_WITH_EXTERNAL_APP, sharedPreferences.getBoolean(DOWNLOAD_WITH_EXTERNAL_APP, false))
+            preferencesContentValues.put(SCROLL_APP_BAR, sharedPreferences.getBoolean(SCROLL_APP_BAR, true))
+            preferencesContentValues.put(BOTTOM_APP_BAR, sharedPreferences.getBoolean(BOTTOM_APP_BAR, false))
+            preferencesContentValues.put(DISPLAY_ADDITIONAL_APP_BAR_ICONS, sharedPreferences.getBoolean(DISPLAY_ADDITIONAL_APP_BAR_ICONS, false))
+            preferencesContentValues.put(APP_THEME, sharedPreferences.getString(APP_THEME, context.getString(R.string.app_theme_default_value)))
+            preferencesContentValues.put(WEBVIEW_THEME, sharedPreferences.getString(WEBVIEW_THEME, context.getString(R.string.webview_theme_default_value)))
+            preferencesContentValues.put(WIDE_VIEWPORT, sharedPreferences.getBoolean(WIDE_VIEWPORT, true))
+            preferencesContentValues.put(DISPLAY_WEBPAGE_IMAGES, sharedPreferences.getBoolean(DISPLAY_WEBPAGE_IMAGES, true))
+
+            // Insert the preferences content values into the temporary export database.
+            temporaryExportDatabase.insert(PREFERENCES_TABLE, null, preferencesContentValues)
+
+            // Close the temporary export database.
+            temporaryExportDatabase.close()
+
+
+            // The file may be copied directly in Kotlin using `File.copyTo`.  <https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/java.io.-file/copy-to.html>
+            // It can be copied in Android using `Files.copy` once the minimum API >= 26.
+            // <https://developer.android.com/reference/java/nio/file/Files#copy(java.nio.file.Path,%20java.nio.file.Path,%20java.nio.file.CopyOption...)>
+            // However, the file cannot be acquired from the content URI until the minimum API >= 29.  <https://developer.android.com/reference/kotlin/android/content/ContentResolver#openfile>
+
+            // Create the temporary export file input stream.
+            val temporaryExportFileInputStream = FileInputStream(temporaryExportFile)
+
+            // Create a byte array.
+            val transferByteArray = ByteArray(1024)
+
+            // Create an integer to track the number of bytes read.
+            var bytesRead: Int
+
+            // Copy the temporary export file to the export file output stream.
+            while (temporaryExportFileInputStream.read(transferByteArray).also { bytesRead = it } > 0) {
+                exportFileOutputStream.write(transferByteArray, 0, bytesRead)
+            }
+
+            // Flush the export file output stream.
+            exportFileOutputStream.flush()
+
+            // Close the file streams.
+            temporaryExportFileInputStream.close()
+            exportFileOutputStream.close()
+
+            // Delete the temporary export file database, journal, and other related auxiliary files.
+            SQLiteDatabase.deleteDatabase(temporaryExportFile)
+
+            // Return the export successful string.
+            EXPORT_SUCCESSFUL
+        } catch (exception: Exception) {
+            // Return the export error.
+            exception.toString()
+        }
+    }
+}
\ No newline at end of file
index f0634b218c332aeb3b2133ce9c53223bdb0f069c..f8de88fd7a067a0e5c16321c5e5bf1f786ae86aa 100644 (file)
@@ -30,7 +30,7 @@ import java.util.concurrent.ExecutorService
 class WebViewSourceFactory (private val urlString: String, private val userAgent: String, private val localeString: String, private val proxy: Proxy, private val contentResolver: ContentResolver,
                             private val executorService: ExecutorService): ViewModelProvider.Factory {
     // Override the create function in order to add the provided arguments.
-    override fun <T: ViewModel?> create(modelClass: Class<T>): T {
+    override fun <T: ViewModel> create(modelClass: Class<T>): T {
         // Return a new instance of the model class with the provided arguments.
         return modelClass.getConstructor(String::class.java, String::class.java, String::class.java, Proxy::class.java, ContentResolver::class.java, ExecutorService::class.java)
                 .newInstance(urlString, userAgent, localeString, proxy, contentResolver, executorService)
index 9edd3bf2eda1c5252de7586b83937e294cacb0e6..ad34be8327baa58234dc08ae8735554eec6ee5f2 100644 (file)
@@ -27,9 +27,9 @@ import android.graphics.drawable.BitmapDrawable
 import android.os.Bundle
 import android.util.AttributeSet
 import android.view.MotionEvent
-import android.webkit.WebView
-import android.webkit.SslErrorHandler
 import android.webkit.HttpAuthHandler
+import android.webkit.SslErrorHandler
+import android.webkit.WebView
 
 import androidx.core.content.ContextCompat
 import androidx.core.view.NestedScrollingChild2
index 02615fd80ebc32435b3bf4caef8e347f3b75f1d3..fcf3537805f7734b2fb6219684be7b22f4b14bf1 100644 (file)
         <string name="view_source">Quelltext anzeigen</string>
         <string name="add_to_home_screen">Zur Startseite hinzufügen</string>
     <string name="share">Teilen</string>
+    <string name="share_message">URL als Nachricht teilen</string>
         <string name="share_url">URL teilen</string>
         <string name="open_with_app">Mit App öffnen</string>
         <string name="open_with_browser">Mit Browser öffnen</string>
index 1c0902e8d9f996f4e1c1e5816c32b709edd7e589..4b6001ce92e29e65bf353b16e935578d15be7244 100644 (file)
         <string name="add_to_home_screen">Añadir a la ventana de inicio</string>
         <string name="view_source">Ver la fuente</string>
     <string name="share">Compartir</string>
+        <string name="share_message">Compartir mensaje</string>
         <string name="share_url">Compartir URL</string>
-        <string name="open_with_app">Abrir con App</string>
-        <string name="open_with_browser">Abrir con Navegador</string>
+        <string name="open_with_app">Abrir con app</string>
+        <string name="open_with_browser">Abrir con navegador</string>
     <string name="add_domain_settings">Añadir dominio</string>
     <string name="edit_domain_settings">Editar dominio</string>
 
         <string name="scroll_app_bar">Desplazar la barra de aplicaciones</string>
         <string name="scroll_app_bar_summary">Desplazar la barra de aplicaciones desde la parte superior de la pantalla cuando el WebView se desplaza hacia abajo.</string>
         <string name="bottom_app_bar">Barra inferior de la app</string>
-        <string name="bottom_app_bar_summary">Mover la barra de apps a la parte inferior de la pantalla.  Al cambiar esta configuración se reiniciará Navegador Privado.</string>
+        <string name="bottom_app_bar_summary">Mover la barra de apps a la parte inferior de la pantalla. Al cambiar esta configuración se reiniciará Navegador Privado.</string>
         <string name="display_additional_app_bar_icons">Mostrar iconos adicionales en la barra de aplicación</string>
         <string name="display_additional_app_bar_icons_summary">Mostrar iconos en la barra de aplicaciones para refrescar el WebView y, si hay espacio,
             para abrir el cajón de marcadores y cambiar las cookies.</string>
index a668434c88e32df2a0b244e214dc7d884e280126..6b0f4b80a5d7e0fc06e07a3270b0b4b16a98b78e 100644 (file)
         <string name="add_to_home_screen">Aggiungi collegamento</string>
         <string name="view_source">Visualizza sorgente</string>
     <string name="share">Condividi</string>
+        <string name="share_message">Condividi messaggio</string>
         <string name="share_url">Condividi URL</string>
-        <string name="open_with_app">Apri con App</string>
-        <string name="open_with_browser">Apri con il Browser</string>
+        <string name="open_with_app">Apri con app</string>
+        <string name="open_with_browser">Apri con il browser</string>
     <string name="add_domain_settings">Aggiungi dominio</string>
     <string name="edit_domain_settings">Modifica dominio</string>
 
         <string name="scroll_app_bar">Permetti lo scrolling della barra dell\'applicazione</string>
         <string name="scroll_app_bar_summary">Permette lo scorrere della barra dell\'applicazione dalla parte alta dello schermo quando si effettua lo scrolling.</string>
         <string name="bottom_app_bar">Barra dell\'app in basso</string>
-        <string name="bottom_app_bar_summary">Sposta la barra dell\'app nella parte bassa dello schermo.  La modifica di questa impostazione provocherà il riavvio di Privacy Browser.</string>
+        <string name="bottom_app_bar_summary">Sposta la barra dell\'app nella parte bassa dello schermo. La modifica di questa impostazione provocherà il riavvio di Privacy Browser.</string>
         <string name="display_additional_app_bar_icons">Mostra icone addizionali nella barra dell\'applicazione</string>
         <string name="display_additional_app_bar_icons_summary">Mostra nella barra dell\'applicazione le icone per l\'aggiornamento della pagina e, se lo spazio è disponibile,
             per aprire i preferiti e per abilitare o disabilitare i cookie.</string>
index 5a7dbe2385e3d6c4c8ff7290921324979c8cd975..2ca765199c89d990930c73ee54f0bbf903f083af 100644 (file)
         <string name="add_to_home_screen">Добавить на главный экран</string>
         <string name="view_source">Просмотр исходного кода</string>
     <string name="share">Поделиться</string>
+        <string name="share_message">Поделиться в сообщении</string>
         <string name="share_url">Поделиться URL</string>
         <string name="open_with_app">Открыть в приложении</string>
         <string name="open_with_browser">Открыть в браузере</string>
index 2ea42e68529a8b12e38179120baa357ca165ade3..ae2d2aacfef260fa2910cf812016ac0f2da5e752 100644 (file)
         <string name="scroll_app_bar">Scroll the app bar</string>
         <string name="scroll_app_bar_summary">Scroll the app bar off the top of the screen when the WebView scrolls down.</string>
         <string name="bottom_app_bar">Bottom app bar</string>
-        <string name="bottom_app_bar_summary">Move the app bar to the bottom of the screen.  Changing this setting will restart Privacy Browser.</string>
+        <string name="bottom_app_bar_summary">Move the app bar to the bottom of the screen. Changing this setting will restart Privacy Browser.</string>
         <string name="display_additional_app_bar_icons">Display additional app bar icons</string>
         <string name="display_additional_app_bar_icons_summary">Display icons in the app bar for refreshing the WebView and, if there is room, for opening the bookmarks drawer and toggling cookies.</string>
         <string name="app_theme">App theme</string>
index 9d5429511429ed0b7734843eb431c5f712399220..e275254add125349bd55b9486e0a5f137758e28e 100644 (file)
@@ -26,8 +26,8 @@ buildscript {
     }
 
     dependencies {
-        classpath 'com.android.tools.build:gradle:7.1.2'
-        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10"
+        classpath 'com.android.tools.build:gradle:7.1.3'
+        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.20"
 
         // NOTE: Do not place your application dependencies here; they belong
         // in the individual module build.gradle files