From 9338dfa2f8a295736d35e4f468f0c0ba573a1b58 Mon Sep 17 00:00:00 2001 From: Soren Stoutner Date: Sat, 9 Apr 2022 15:03:51 -0700 Subject: [PATCH] Fix the WebView layout with bottom app bars. https://redmine.stoutner.com/issues/839 --- app/build.gradle | 2 +- .../activities/ImportExportActivity.java | 2 + .../activities/MainWebViewActivity.java | 156 ++-- .../fragments/AboutWebViewFragment.kt | 1 + .../fragments/SettingsFragment.java | 15 +- .../helpers/BookmarksDatabaseHelper.kt | 8 +- .../helpers/DomainsDatabaseHelper.kt | 232 ++--- .../helpers/ImportExportDatabaseHelper.java | 794 ----------------- .../helpers/ImportExportDatabaseHelper.kt | 829 ++++++++++++++++++ .../WebViewSourceFactory.kt | 2 +- .../views/NestedScrollWebView.kt | 4 +- app/src/main/res/values-de/strings.xml | 1 + app/src/main/res/values-es/strings.xml | 7 +- app/src/main/res/values-it/strings.xml | 7 +- app/src/main/res/values-ru/strings.xml | 1 + app/src/main/res/values/strings.xml | 2 +- build.gradle | 4 +- 17 files changed, 1080 insertions(+), 987 deletions(-) delete mode 100644 app/src/main/java/com/stoutner/privacybrowser/helpers/ImportExportDatabaseHelper.java create mode 100644 app/src/main/java/com/stoutner/privacybrowser/helpers/ImportExportDatabaseHelper.kt diff --git a/app/build.gradle b/app/build.gradle index 8469c24d..95473938 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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' diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/ImportExportActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/ImportExportActivity.java index fe4f99a0..548316dd 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/ImportExportActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/ImportExportActivity.java @@ -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. 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. OutputStream exportFileOutputStream = getContentResolver().openOutputStream(Uri.parse(noEncryptionFileNameString)); // Export the unencrypted file. diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java index 45a1de5b..8344d5c4 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java @@ -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); diff --git a/app/src/main/java/com/stoutner/privacybrowser/fragments/AboutWebViewFragment.kt b/app/src/main/java/com/stoutner/privacybrowser/fragments/AboutWebViewFragment.kt index 3f14e183..3b516ec5 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/fragments/AboutWebViewFragment.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/fragments/AboutWebViewFragment.kt @@ -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) diff --git a/app/src/main/java/com/stoutner/privacybrowser/fragments/SettingsFragment.java b/app/src/main/java/com/stoutner/privacybrowser/fragments/SettingsFragment.java index 05b82dff..e17f8272 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/fragments/SettingsFragment.java +++ b/app/src/main/java/com/stoutner/privacybrowser/fragments/SettingsFragment.java @@ -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(); diff --git a/app/src/main/java/com/stoutner/privacybrowser/helpers/BookmarksDatabaseHelper.kt b/app/src/main/java/com/stoutner/privacybrowser/helpers/BookmarksDatabaseHelper.kt index 12753e16..135827ec 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/helpers/BookmarksDatabaseHelper.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/helpers/BookmarksDatabaseHelper.kt @@ -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, " + diff --git a/app/src/main/java/com/stoutner/privacybrowser/helpers/DomainsDatabaseHelper.kt b/app/src/main/java/com/stoutner/privacybrowser/helpers/DomainsDatabaseHelper.kt index 4fc6a081..53a3cbe4 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/helpers/DomainsDatabaseHelper.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/helpers/DomainsDatabaseHelper.kt @@ -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. + // + 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. - // It will be a while before that is supported in Android. - // 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. + // It will be a while before that is supported in Android. + // 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 index 096364f6..00000000 --- a/app/src/main/java/com/stoutner/privacybrowser/helpers/ImportExportDatabaseHelper.java +++ /dev/null @@ -1,794 +0,0 @@ -/* - * Copyright © 2018-2022 Soren Stoutner . - * - * This file is part of 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 . - */ - -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. - // - // 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. - // - // 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 index 00000000..13abb32a --- /dev/null +++ b/app/src/main/java/com/stoutner/privacybrowser/helpers/ImportExportDatabaseHelper.kt @@ -0,0 +1,829 @@ +/* + * Copyright © 2018-2022 Soren Stoutner . + * + * This file is part of 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 . + */ + +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`. + // It can be copied in Android using `Files.copy` once the minimum API >= 26. + // + // However, the file cannot be acquired from the content URI until the minimum API >= 29. + + // 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. + // + // 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. + // + 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. + // + 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. + // + 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. + // + 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 = 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. + // + 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. + // + // 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. + // + 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`. + // It can be copied in Android using `Files.copy` once the minimum API >= 26. + // + // However, the file cannot be acquired from the content URI until the minimum API >= 29. + + // 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 diff --git a/app/src/main/java/com/stoutner/privacybrowser/viewmodelfactories/WebViewSourceFactory.kt b/app/src/main/java/com/stoutner/privacybrowser/viewmodelfactories/WebViewSourceFactory.kt index f0634b21..f8de88fd 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/viewmodelfactories/WebViewSourceFactory.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/viewmodelfactories/WebViewSourceFactory.kt @@ -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 create(modelClass: Class): T { + override fun create(modelClass: Class): 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) diff --git a/app/src/main/java/com/stoutner/privacybrowser/views/NestedScrollWebView.kt b/app/src/main/java/com/stoutner/privacybrowser/views/NestedScrollWebView.kt index 9edd3bf2..ad34be83 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/views/NestedScrollWebView.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/views/NestedScrollWebView.kt @@ -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 diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 02615fd8..fcf35378 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -167,6 +167,7 @@ Quelltext anzeigen Zur Startseite hinzufügen Teilen + URL als Nachricht teilen URL teilen Mit App öffnen Mit Browser öffnen diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 1c0902e8..4b6001ce 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -163,9 +163,10 @@ Añadir a la ventana de inicio Ver la fuente Compartir + Compartir mensaje Compartir URL - Abrir con App - Abrir con Navegador + Abrir con app + Abrir con navegador Añadir dominio Editar dominio @@ -569,7 +570,7 @@ Desplazar la barra de aplicaciones Desplazar la barra de aplicaciones desde la parte superior de la pantalla cuando el WebView se desplaza hacia abajo. Barra inferior de la app - Mover la barra de apps a la parte inferior de la pantalla. Al cambiar esta configuración se reiniciará Navegador Privado. + Mover la barra de apps a la parte inferior de la pantalla. Al cambiar esta configuración se reiniciará Navegador Privado. Mostrar iconos adicionales en la barra de aplicación 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. diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index a668434c..6b0f4b80 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -163,9 +163,10 @@ Aggiungi collegamento Visualizza sorgente Condividi + Condividi messaggio Condividi URL - Apri con App - Apri con il Browser + Apri con app + Apri con il browser Aggiungi dominio Modifica dominio @@ -568,7 +569,7 @@ Permetti lo scrolling della barra dell\'applicazione Permette lo scorrere della barra dell\'applicazione dalla parte alta dello schermo quando si effettua lo scrolling. Barra dell\'app in basso - Sposta la barra dell\'app nella parte bassa dello schermo. La modifica di questa impostazione provocherà il riavvio di Privacy Browser. + Sposta la barra dell\'app nella parte bassa dello schermo. La modifica di questa impostazione provocherà il riavvio di Privacy Browser. Mostra icone addizionali nella barra dell\'applicazione 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. diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 5a7dbe23..2ca76519 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -160,6 +160,7 @@ Добавить на главный экран Просмотр исходного кода Поделиться + Поделиться в сообщении Поделиться URL Открыть в приложении Открыть в браузере diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2ea42e68..ae2d2aac 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -619,7 +619,7 @@ Scroll the app bar Scroll the app bar off the top of the screen when the WebView scrolls down. Bottom app bar - Move the app bar to the bottom of the screen. Changing this setting will restart Privacy Browser. + Move the app bar to the bottom of the screen. Changing this setting will restart Privacy Browser. Display additional app bar icons Display icons in the app bar for refreshing the WebView and, if there is room, for opening the bookmarks drawer and toggling cookies. App theme diff --git a/build.gradle b/build.gradle index 9d542951..e275254a 100644 --- a/build.gradle +++ b/build.gradle @@ -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 -- 2.45.2