2 * Copyright © 2018-2022 Soren Stoutner <soren@stoutner.com>.
4 * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
6 * Privacy Browser Android is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * Privacy Browser Android is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with Privacy Browser Android. If not, see <http://www.gnu.org/licenses/>.
20 package com.stoutner.privacybrowser.helpers
22 import android.content.ContentValues
23 import android.content.Context
24 import android.database.DatabaseUtils
25 import android.database.sqlite.SQLiteDatabase
27 import androidx.preference.PreferenceManager
29 import com.stoutner.privacybrowser.R
32 import java.io.FileInputStream
33 import java.io.FileOutputStream
34 import java.io.InputStream
35 import java.io.OutputStream
37 // Define the private class constants.
38 private const val SCHEMA_VERSION = 14
39 private const val PREFERENCES_TABLE = "preferences"
41 // Define the private preferences constants.
42 private const val ID = "_id"
43 private const val JAVASCRIPT = "javascript"
44 private const val COOKIES = "cookies"
45 private const val DOM_STORAGE = "dom_storage"
46 private const val SAVE_FORM_DATA = "save_form_data"
47 private const val USER_AGENT = "user_agent"
48 private const val CUSTOM_USER_AGENT = "custom_user_agent"
49 private const val INCOGNITO_MODE = "incognito_mode"
50 private const val ALLOW_SCREENSHOTS = "allow_screenshots"
51 private const val EASYLIST = "easylist"
52 private const val EASYPRIVACY = "easyprivacy"
53 private const val FANBOYS_ANNOYANCE_LIST = "fanboys_annoyance_list"
54 private const val FANBOYS_SOCIAL_BLOCKING_LIST = "fanboys_social_blocking_list"
55 private const val ULTRALIST = "ultralist"
56 private const val ULTRAPRIVACY = "ultraprivacy"
57 private const val BLOCK_ALL_THIRD_PARTY_REQUESTS = "block_all_third_party_requests"
58 private const val GOOGLE_ANALYTICS = "google_analytics"
59 private const val FACEBOOK_CLICK_IDS = "facebook_click_ids"
60 private const val TWITTER_AMP_REDIRECTS = "twitter_amp_redirects"
61 private const val SEARCH = "search"
62 private const val SEARCH_CUSTOM_URL = "search_custom_url"
63 private const val PROXY = "proxy"
64 private const val PROXY_CUSTOM_URL = "proxy_custom_url"
65 private const val FULL_SCREEN_BROWSING_MODE = "full_screen_browsing_mode"
66 private const val HIDE_APP_BAR = "hide_app_bar"
67 private const val CLEAR_EVERYTHING = "clear_everything"
68 private const val CLEAR_COOKIES = "clear_cookies"
69 private const val CLEAR_DOM_STORAGE = "clear_dom_storage"
70 private const val CLEAR_FORM_DATA = "clear_form_data"
71 private const val CLEAR_LOGCAT = "clear_logcat"
72 private const val CLEAR_CACHE = "clear_cache"
73 private const val HOMEPAGE = "homepage"
74 private const val FONT_SIZE = "font_size"
75 private const val OPEN_INTENTS_IN_NEW_TAB = "open_intents_in_new_tab"
76 private const val SWIPE_TO_REFRESH = "swipe_to_refresh"
77 private const val DOWNLOAD_WITH_EXTERNAL_APP = "download_with_external_app"
78 private const val SCROLL_APP_BAR = "scroll_app_bar"
79 private const val BOTTOM_APP_BAR = "bottom_app_bar"
80 private const val DISPLAY_ADDITIONAL_APP_BAR_ICONS = "display_additional_app_bar_icons"
81 private const val APP_THEME = "app_theme"
82 private const val WEBVIEW_THEME = "webview_theme"
83 private const val WIDE_VIEWPORT = "wide_viewport"
84 private const val DISPLAY_WEBPAGE_IMAGES = "display_webpage_images"
86 class ImportExportDatabaseHelper {
87 // Define the public companion object constants. These can be moved to public class constants once the entire project has migrated to Kotlin.
89 // Define the public class constants.
90 const val EXPORT_SUCCESSFUL = "Export Successful"
91 const val IMPORT_SUCCESSFUL = "Import Successful"
94 fun importUnencrypted(importFileInputStream: InputStream, context: Context): String {
96 // Create a temporary import file.
97 val temporaryImportFile = File.createTempFile("temporary_import_file", null, context.cacheDir)
99 // The file may be copied directly in Kotlin using `File.copyTo`. <https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/java.io.-file/copy-to.html>
100 // It can be copied in Android using `Files.copy` once the minimum API >= 26.
101 // <https://developer.android.com/reference/java/nio/file/Files#copy(java.nio.file.Path,%20java.nio.file.Path,%20java.nio.file.CopyOption...)>
102 // However, the file cannot be acquired from the content URI until the minimum API >= 29. <https://developer.android.com/reference/kotlin/android/content/ContentResolver#openfile>
104 // Create a temporary file output stream.
105 val temporaryImportFileOutputStream = FileOutputStream(temporaryImportFile)
107 // Create a transfer byte array.
108 val transferByteArray = ByteArray(1024)
110 // Create an integer to track the number of bytes read.
113 // Copy the import file to the temporary import file.
114 while (importFileInputStream.read(transferByteArray).also { bytesRead = it } > 0) {
115 temporaryImportFileOutputStream.write(transferByteArray, 0, bytesRead)
118 // Flush the temporary import file output stream.
119 temporaryImportFileOutputStream.flush()
121 // Close the file streams.
122 importFileInputStream.close()
123 temporaryImportFileOutputStream.close()
126 // Get a handle for the shared preference.
127 val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
129 // Open the import database. Once the minimum API >= 27 the file can be opened directly without using the string.
130 val importDatabase = SQLiteDatabase.openDatabase(temporaryImportFile.toString(), null, SQLiteDatabase.OPEN_READWRITE)
132 // Get the database version.
133 val importDatabaseVersion = importDatabase.version
135 // Upgrade from schema version 1, first used in Privacy Browser 2.13, to schema version 2, first used in Privacy Browser 2.14.
136 // Previously this upgrade added `download_with_external_app` to the Preferences table. But that is now removed in schema version 10.
138 // Upgrade from schema version 2, first used in Privacy Browser 2.14, to schema version 3, first used in Privacy Browser 2.15.
139 if (importDatabaseVersion < 3) {
140 // Once the SQLite version is >= 3.25.0 (API >= 30) `ALTER TABLE RENAME COLUMN` can be used. <https://www.sqlite.org/lang_altertable.html> <https://www.sqlite.org/changes.html>
141 // <https://developer.android.com/reference/android/database/sqlite/package-summary>
142 // 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.
144 // Create the new font size column.
145 importDatabase.execSQL("ALTER TABLE $PREFERENCES_TABLE ADD COLUMN $FONT_SIZE TEXT")
147 // Populate the preferences table with the current font size value.
148 importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $FONT_SIZE = default_font_size")
151 // Upgrade from schema version 3, first used in Privacy Browser 2.15, to schema version 4, first used in Privacy Browser 2.16.
152 if (importDatabaseVersion < 4) {
153 // Add the Pinned IP Addresses columns to the domains table.
154 importDatabase.execSQL("ALTER TABLE " + DomainsDatabaseHelper.DOMAINS_TABLE + " ADD COLUMN " + DomainsDatabaseHelper.PINNED_IP_ADDRESSES + " BOOLEAN")
155 importDatabase.execSQL("ALTER TABLE " + DomainsDatabaseHelper.DOMAINS_TABLE + " ADD COLUMN " + DomainsDatabaseHelper.IP_ADDRESSES + " TEXT")
158 // Upgrade from schema version 4, first used in Privacy Browser 2.16, to schema version 5, first used in Privacy Browser 2.17.
159 if (importDatabaseVersion < 5) {
160 // Add the hide and scroll app bar columns to the preferences table.
161 importDatabase.execSQL("ALTER TABLE $PREFERENCES_TABLE ADD COLUMN $HIDE_APP_BAR BOOLEAN")
162 importDatabase.execSQL("ALTER TABLE $PREFERENCES_TABLE ADD COLUMN $SCROLL_APP_BAR BOOLEAN")
164 // Get the current hide and scroll app bar settings.
165 val hideAppBar = sharedPreferences.getBoolean(HIDE_APP_BAR, true)
166 val scrollAppBar = sharedPreferences.getBoolean(SCROLL_APP_BAR, true)
168 // Populate the preferences table with the current app bar values.
169 // This can switch to using the variables directly once the API >= 30. <https://www.sqlite.org/datatype3.html#boolean_datatype>
170 // <https://developer.android.com/reference/android/database/sqlite/package-summary>
172 importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $HIDE_APP_BAR = 1")
174 importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $HIDE_APP_BAR = 0")
178 importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $SCROLL_APP_BAR = 1")
180 importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $SCROLL_APP_BAR = 0")
184 // Upgrade from schema version 5, first used in Privacy Browser 2.17, to schema version 6, first used in Privacy Browser 3.0.
185 if (importDatabaseVersion < 6) {
186 // Add the open intents in new tab column to the preferences table.
187 importDatabase.execSQL("ALTER TABLE $PREFERENCES_TABLE ADD COLUMN $OPEN_INTENTS_IN_NEW_TAB BOOLEAN")
189 // Get the current open intents in new tab preference.
190 val openIntentsInNewTab = sharedPreferences.getBoolean(OPEN_INTENTS_IN_NEW_TAB, true)
192 // Populate the preferences table with the current open intents value.
193 // This can switch to using the variables directly once the API >= 30. <https://www.sqlite.org/datatype3.html#boolean_datatype>
194 // <https://developer.android.com/reference/android/database/sqlite/package-summary>
195 if (openIntentsInNewTab) {
196 importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $OPEN_INTENTS_IN_NEW_TAB = 1")
198 importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $OPEN_INTENTS_IN_NEW_TAB = 0")
202 // Upgrade from schema version 6, first used in Privacy Browser 3.0, to schema version 7, first used in Privacy Browser 3.1.
203 if (importDatabaseVersion < 7) {
204 // Add the wide viewport column to the domains table.
205 importDatabase.execSQL("ALTER TABLE " + DomainsDatabaseHelper.DOMAINS_TABLE + " ADD COLUMN " + DomainsDatabaseHelper.WIDE_VIEWPORT + " INTEGER")
207 // Add the Google Analytics, Facebook Click IDs, Twitter AMP redirects, and wide viewport columns to the preferences table.
208 importDatabase.execSQL("ALTER TABLE $PREFERENCES_TABLE ADD COLUMN $GOOGLE_ANALYTICS BOOLEAN")
209 importDatabase.execSQL("ALTER TABLE $PREFERENCES_TABLE ADD COLUMN $FACEBOOK_CLICK_IDS BOOLEAN")
210 importDatabase.execSQL("ALTER TABLE $PREFERENCES_TABLE ADD COLUMN $TWITTER_AMP_REDIRECTS BOOLEAN")
211 importDatabase.execSQL("ALTER TABLE $PREFERENCES_TABLE ADD COLUMN $WIDE_VIEWPORT BOOLEAN")
213 // Get the current preference values.
214 val googleAnalytics = sharedPreferences.getBoolean(GOOGLE_ANALYTICS, true)
215 val facebookClickIds = sharedPreferences.getBoolean(FACEBOOK_CLICK_IDS, true)
216 val twitterAmpRedirects = sharedPreferences.getBoolean(TWITTER_AMP_REDIRECTS, true)
217 val wideViewport = sharedPreferences.getBoolean(WIDE_VIEWPORT, true)
219 // Populate the preferences with the current Google Analytics value.
220 // This can switch to using the variables directly once the API >= 30. <https://www.sqlite.org/datatype3.html#boolean_datatype>
221 // <https://developer.android.com/reference/android/database/sqlite/package-summary>
222 if (googleAnalytics) {
223 importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $GOOGLE_ANALYTICS = 1")
225 importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $GOOGLE_ANALYTICS = 0")
228 // Populate the preferences with the current Facebook Click IDs value.
229 if (facebookClickIds) {
230 importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $FACEBOOK_CLICK_IDS = 1")
232 importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $FACEBOOK_CLICK_IDS = 0")
235 // Populate the preferences table with the current Twitter AMP redirects value.
236 if (twitterAmpRedirects) {
237 importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $TWITTER_AMP_REDIRECTS = 1")
239 importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $TWITTER_AMP_REDIRECTS = 0")
242 // Populate the preferences table with the current wide viewport value.
244 importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $WIDE_VIEWPORT = 1")
246 importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $WIDE_VIEWPORT = 0")
250 // Upgrade from schema version 7, first used in Privacy Browser 3.1, to schema version 8, first used in Privacy Browser 3.2.
251 if (importDatabaseVersion < 8) {
252 // Add the UltraList column to the tables.
253 importDatabase.execSQL("ALTER TABLE " + DomainsDatabaseHelper.DOMAINS_TABLE + " ADD COLUMN " + DomainsDatabaseHelper.ULTRALIST + " BOOLEAN")
254 importDatabase.execSQL("ALTER TABLE $PREFERENCES_TABLE ADD COLUMN $ULTRALIST BOOLEAN")
256 // Get the current preference values.
257 val ultraList = sharedPreferences.getBoolean(ULTRALIST, true)
259 // Populate the tables with the current UltraList value.
260 // This can switch to using the variables directly once the API >= 30. <https://www.sqlite.org/datatype3.html#boolean_datatype>
261 // <https://developer.android.com/reference/android/database/sqlite/package-summary>
263 importDatabase.execSQL("UPDATE " + DomainsDatabaseHelper.DOMAINS_TABLE + " SET " + DomainsDatabaseHelper.ULTRALIST + " = " + 1)
264 importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $ULTRALIST = 1")
266 importDatabase.execSQL("UPDATE " + DomainsDatabaseHelper.DOMAINS_TABLE + " SET " + DomainsDatabaseHelper.ULTRALIST + " = " + 0)
267 importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $ULTRALIST = 0")
271 // Upgrade from schema version 8, first used in Privacy Browser 3.2, to schema version 9, first used in Privacy Browser 3.3.
272 if (importDatabaseVersion < 9) {
273 // Add the new proxy columns to the preferences table.
274 importDatabase.execSQL("ALTER TABLE $PREFERENCES_TABLE ADD COLUMN $PROXY TEXT")
275 importDatabase.execSQL("ALTER TABLE $PREFERENCES_TABLE ADD COLUMN $PROXY_CUSTOM_URL TEXT")
277 // Get the current proxy values.
278 val proxy = sharedPreferences.getString(PROXY, context.getString(R.string.proxy_default_value))
279 var proxyCustomUrl = sharedPreferences.getString(PROXY_CUSTOM_URL, context.getString(R.string.proxy_custom_url_default_value))
281 // SQL escape the proxy custom URL string.
282 proxyCustomUrl = DatabaseUtils.sqlEscapeString(proxyCustomUrl)
284 // 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.
285 importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $PROXY = '$proxy'")
286 importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $PROXY_CUSTOM_URL = $proxyCustomUrl")
289 // Upgrade from schema version 9, first used in Privacy Browser 3.3, to schema version 10, first used in Privacy Browser 3.4.
290 // Previously this upgrade added `download_location` and `download_custom_location` to the Preferences table. But they were removed in schema version 13.
292 // Upgrade from schema version 10, first used in Privacy Browser 3.4, to schema version 11, first used in Privacy Browser 3.5.
293 if (importDatabaseVersion < 11) {
294 // Add the app theme column to the preferences table.
295 importDatabase.execSQL("ALTER TABLE $PREFERENCES_TABLE ADD COLUMN $APP_THEME TEXT")
297 // Get a cursor for the dark theme preference.
298 val darkThemePreferencesCursor = importDatabase.rawQuery("SELECT dark_theme FROM $PREFERENCES_TABLE", null)
300 // Move to the first entry.
301 darkThemePreferencesCursor.moveToFirst()
303 // Get the old dark theme value, which is in column 0.
304 val darkTheme = darkThemePreferencesCursor.getInt(0)
306 // Close the dark theme preference cursor.
307 darkThemePreferencesCursor.close()
309 // Get the system default string.
310 val systemDefault = context.getString(R.string.app_theme_default_value)
312 // Get the theme entry values string array.
313 val appThemeEntryValuesStringArray: Array<String> = context.resources.getStringArray(R.array.app_theme_entry_values)
315 // Get the dark string.
316 val dark = appThemeEntryValuesStringArray[2]
318 // Populate the app theme according to the old dark theme preference.
319 if (darkTheme == 0) { // A light theme was selected.
320 // Set the app theme to be the system default.
321 importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $APP_THEME = '$systemDefault'")
322 } else { // A dark theme was selected.
323 // Set the app theme to be dark.
324 importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $APP_THEME = '$dark'")
327 // 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.
328 importDatabase.execSQL("ALTER TABLE " + DomainsDatabaseHelper.DOMAINS_TABLE + " ADD COLUMN " + DomainsDatabaseHelper.WEBVIEW_THEME + " INTEGER")
330 // Add the WebView theme to the preferences table.
331 importDatabase.execSQL("ALTER TABLE $PREFERENCES_TABLE ADD COLUMN $WEBVIEW_THEME TEXT")
333 // Get the WebView theme default value string.
334 val webViewThemeDefaultValue = context.getString(R.string.webview_theme_default_value)
336 // Set the WebView theme in the preferences table to be the default.
337 importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $WEBVIEW_THEME = '$webViewThemeDefaultValue'")
340 // Upgrade from schema version 11, first used in Privacy Browser 3.5, to schema version 12, first used in Privacy Browser 3.6.
341 if (importDatabaseVersion < 12) {
342 // Add the clear logcat column to the preferences table.
343 importDatabase.execSQL("ALTER TABLE $PREFERENCES_TABLE ADD COLUMN $CLEAR_LOGCAT BOOLEAN")
345 // Get the current clear logcat value.
346 val clearLogcat = sharedPreferences.getBoolean(CLEAR_LOGCAT, true)
348 // Populate the preferences table with the current clear logcat value.
349 // This can switch to using the variables directly once the API >= 30. <https://www.sqlite.org/datatype3.html#boolean_datatype>
350 // <https://developer.android.com/reference/android/database/sqlite/package-summary>
352 importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $CLEAR_LOGCAT = 1")
354 importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $CLEAR_LOGCAT = 0")
358 // Upgrade from schema version 12, first used in Privacy Browser 3.6, to schema version 13, first used in Privacy Browser 3.7.
359 // 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.
361 // Upgrade from schema version 13, first used in Privacy Browser 3.7, to schema version 14, first used in Privacy Browser 3.8.
362 if (importDatabaseVersion < 14) {
363 // `enabledthirdpartycookies` was removed from the domains table. `do_not_track` and `third_party_cookies` were removed from the preferences table.
365 // Once the SQLite version is >= 3.25.0 `ALTER TABLE RENAME COLUMN` can be used. <https://www.sqlite.org/lang_altertable.html> <https://www.sqlite.org/changes.html>
366 // <https://developer.android.com/reference/android/database/sqlite/package-summary>
367 // 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.
369 // Create the new cookies columns.
370 importDatabase.execSQL("ALTER TABLE " + DomainsDatabaseHelper.DOMAINS_TABLE + " ADD COLUMN " + DomainsDatabaseHelper.COOKIES + " BOOLEAN")
371 importDatabase.execSQL("ALTER TABLE $PREFERENCES_TABLE ADD COLUMN $COOKIES BOOLEAN")
373 // Copy the data from the old cookies columns to the new ones.
374 importDatabase.execSQL("UPDATE " + DomainsDatabaseHelper.DOMAINS_TABLE + " SET " + DomainsDatabaseHelper.COOKIES + " = enablefirstpartycookies")
375 importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $COOKIES = first_party_cookies")
377 // Create the new download with external app and bottom app bar columns.
378 importDatabase.execSQL("ALTER TABLE $PREFERENCES_TABLE ADD COLUMN $DOWNLOAD_WITH_EXTERNAL_APP BOOLEAN")
379 importDatabase.execSQL("ALTER TABLE $PREFERENCES_TABLE ADD COLUMN $BOTTOM_APP_BAR BOOLEAN")
381 // Get the current values for the new columns.
382 val downloadWithExternalApp = sharedPreferences.getBoolean(DOWNLOAD_WITH_EXTERNAL_APP, false)
383 val bottomAppBar = sharedPreferences.getBoolean(BOTTOM_APP_BAR, false)
385 // Populate the preferences table with the current download with external app value.
386 // This can switch to using the variables directly once the API >= 30. <https://www.sqlite.org/datatype3.html#boolean_datatype>
387 // <https://developer.android.com/reference/android/database/sqlite/package-summary>
388 if (downloadWithExternalApp) {
389 importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $DOWNLOAD_WITH_EXTERNAL_APP = 1")
391 importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $DOWNLOAD_WITH_EXTERNAL_APP = 0")
394 // Populate the preferences table with the current bottom app bar value.
396 importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $BOTTOM_APP_BAR = 1")
398 importDatabase.execSQL("UPDATE $PREFERENCES_TABLE SET $BOTTOM_APP_BAR = 0")
402 // Get a cursor for the bookmarks table.
403 val importBookmarksCursor = importDatabase.rawQuery("SELECT * FROM " + BookmarksDatabaseHelper.BOOKMARKS_TABLE, null)
405 // Delete the current bookmarks database.
406 context.deleteDatabase(BookmarksDatabaseHelper.BOOKMARKS_DATABASE)
408 // Create a new bookmarks database.
409 val bookmarksDatabaseHelper = BookmarksDatabaseHelper(context)
411 // Move to the first record.
412 importBookmarksCursor.moveToFirst()
414 // Copy the data from the import bookmarks cursor into the bookmarks database.
415 for (i in 0 until importBookmarksCursor.count) {
416 // Create a bookmark content values.
417 val bookmarkContentValues = ContentValues()
419 // Add the information for this bookmark to the content values.
420 bookmarkContentValues.put(BookmarksDatabaseHelper.BOOKMARK_NAME, importBookmarksCursor.getString(importBookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_NAME)))
421 bookmarkContentValues.put(BookmarksDatabaseHelper.BOOKMARK_URL, importBookmarksCursor.getString(importBookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_URL)))
422 bookmarkContentValues.put(BookmarksDatabaseHelper.PARENT_FOLDER, importBookmarksCursor.getString(importBookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.PARENT_FOLDER)))
423 bookmarkContentValues.put(BookmarksDatabaseHelper.DISPLAY_ORDER, importBookmarksCursor.getInt(importBookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.DISPLAY_ORDER)))
424 bookmarkContentValues.put(BookmarksDatabaseHelper.IS_FOLDER, importBookmarksCursor.getInt(importBookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.IS_FOLDER)))
425 bookmarkContentValues.put(BookmarksDatabaseHelper.FAVORITE_ICON, importBookmarksCursor.getBlob(importBookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.FAVORITE_ICON)))
427 // Insert the content values into the bookmarks database.
428 bookmarksDatabaseHelper.createBookmark(bookmarkContentValues)
430 // Advance to the next record.
431 importBookmarksCursor.moveToNext()
434 // Close the bookmarks cursor and database.
435 importBookmarksCursor.close()
436 bookmarksDatabaseHelper.close()
439 // Get a cursor for the domains table.
440 val importDomainsCursor = importDatabase.rawQuery("SELECT * FROM " + DomainsDatabaseHelper.DOMAINS_TABLE + " ORDER BY " + DomainsDatabaseHelper.DOMAIN_NAME + " ASC", null)
442 // Delete the current domains database.
443 context.deleteDatabase(DomainsDatabaseHelper.DOMAINS_DATABASE)
445 // Create a new domains database.
446 val domainsDatabaseHelper = DomainsDatabaseHelper(context)
448 // Move to the first record.
449 importDomainsCursor.moveToFirst()
451 // Copy the data from the import domains cursor into the domains database.
452 for (i in 0 until importDomainsCursor.count) {
453 // Create a domain content values.
454 val domainContentValues = ContentValues()
456 // Populate the domain content values.
457 domainContentValues.put(DomainsDatabaseHelper.DOMAIN_NAME, importDomainsCursor.getString(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DOMAIN_NAME)))
458 domainContentValues.put(DomainsDatabaseHelper.ENABLE_JAVASCRIPT, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_JAVASCRIPT)))
459 domainContentValues.put(DomainsDatabaseHelper.COOKIES, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.COOKIES)))
460 domainContentValues.put(DomainsDatabaseHelper.ENABLE_DOM_STORAGE, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_DOM_STORAGE)))
461 domainContentValues.put(DomainsDatabaseHelper.ENABLE_FORM_DATA, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FORM_DATA)))
462 domainContentValues.put(DomainsDatabaseHelper.ENABLE_EASYLIST, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_EASYLIST)))
463 domainContentValues.put(DomainsDatabaseHelper.ENABLE_EASYPRIVACY, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_EASYPRIVACY)))
464 domainContentValues.put(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST,
465 importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST)))
466 domainContentValues.put(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST,
467 importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST)))
468 domainContentValues.put(DomainsDatabaseHelper.ULTRALIST, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ULTRALIST)))
469 domainContentValues.put(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY)))
470 domainContentValues.put(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS,
471 importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS)))
472 domainContentValues.put(DomainsDatabaseHelper.USER_AGENT, importDomainsCursor.getString(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.USER_AGENT)))
473 domainContentValues.put(DomainsDatabaseHelper.FONT_SIZE, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.FONT_SIZE)))
474 domainContentValues.put(DomainsDatabaseHelper.SWIPE_TO_REFRESH, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SWIPE_TO_REFRESH)))
475 domainContentValues.put(DomainsDatabaseHelper.WEBVIEW_THEME, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.WEBVIEW_THEME)))
476 domainContentValues.put(DomainsDatabaseHelper.WIDE_VIEWPORT, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.WIDE_VIEWPORT)))
477 domainContentValues.put(DomainsDatabaseHelper.DISPLAY_IMAGES, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DISPLAY_IMAGES)))
478 domainContentValues.put(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE)))
479 domainContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME,
480 importDomainsCursor.getString(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME)))
481 domainContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION,
482 importDomainsCursor.getString(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION)))
483 domainContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT,
484 importDomainsCursor.getString(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT)))
485 domainContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME,
486 importDomainsCursor.getString(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME)))
487 domainContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION,
488 importDomainsCursor.getString(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION)))
489 domainContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT,
490 importDomainsCursor.getString(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT)))
491 domainContentValues.put(DomainsDatabaseHelper.SSL_START_DATE, importDomainsCursor.getLong(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_START_DATE)))
492 domainContentValues.put(DomainsDatabaseHelper.SSL_END_DATE, importDomainsCursor.getLong(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_END_DATE)))
493 domainContentValues.put(DomainsDatabaseHelper.PINNED_IP_ADDRESSES, importDomainsCursor.getInt(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.PINNED_IP_ADDRESSES)))
494 domainContentValues.put(DomainsDatabaseHelper.IP_ADDRESSES, importDomainsCursor.getString(importDomainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.IP_ADDRESSES)))
496 // Insert the content values into the domains database.
497 domainsDatabaseHelper.addDomain(domainContentValues)
499 // Advance to the next record.
500 importDomainsCursor.moveToNext()
503 // Close the domains cursor and database.
504 importDomainsCursor.close()
505 domainsDatabaseHelper.close()
508 // Get a cursor for the preferences table.
509 val importPreferencesCursor = importDatabase.rawQuery("SELECT * FROM $PREFERENCES_TABLE", null)
511 // Move to the first record.
512 importPreferencesCursor.moveToFirst()
514 // Import the preference data.
515 sharedPreferences.edit()
516 .putBoolean(JAVASCRIPT, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(JAVASCRIPT)) == 1)
517 .putBoolean(COOKIES, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(COOKIES)) == 1)
518 .putBoolean(DOM_STORAGE, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(DOM_STORAGE)) == 1)
519 .putBoolean(SAVE_FORM_DATA, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(SAVE_FORM_DATA)) == 1) // Save form data can be removed once the minimum API >= 26.
520 .putString(USER_AGENT, importPreferencesCursor.getString(importPreferencesCursor.getColumnIndexOrThrow(USER_AGENT)))
521 .putString(CUSTOM_USER_AGENT, importPreferencesCursor.getString(importPreferencesCursor.getColumnIndexOrThrow(CUSTOM_USER_AGENT)))
522 .putBoolean(INCOGNITO_MODE, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(INCOGNITO_MODE)) == 1)
523 .putBoolean(ALLOW_SCREENSHOTS, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(ALLOW_SCREENSHOTS)) == 1)
524 .putBoolean(EASYLIST, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(EASYLIST)) == 1)
525 .putBoolean(EASYPRIVACY, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(EASYPRIVACY)) == 1)
526 .putBoolean(FANBOYS_ANNOYANCE_LIST, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(FANBOYS_ANNOYANCE_LIST)) == 1)
527 .putBoolean(FANBOYS_SOCIAL_BLOCKING_LIST, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(FANBOYS_SOCIAL_BLOCKING_LIST)) == 1)
528 .putBoolean(ULTRALIST, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(ULTRALIST)) == 1)
529 .putBoolean(ULTRAPRIVACY, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(ULTRAPRIVACY)) == 1)
530 .putBoolean(BLOCK_ALL_THIRD_PARTY_REQUESTS, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(BLOCK_ALL_THIRD_PARTY_REQUESTS)) == 1)
531 .putBoolean(GOOGLE_ANALYTICS, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(GOOGLE_ANALYTICS)) == 1)
532 .putBoolean(FACEBOOK_CLICK_IDS, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(FACEBOOK_CLICK_IDS)) == 1)
533 .putBoolean(TWITTER_AMP_REDIRECTS, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(TWITTER_AMP_REDIRECTS)) == 1)
534 .putString(SEARCH, importPreferencesCursor.getString(importPreferencesCursor.getColumnIndexOrThrow(SEARCH)))
535 .putString(SEARCH_CUSTOM_URL, importPreferencesCursor.getString(importPreferencesCursor.getColumnIndexOrThrow(SEARCH_CUSTOM_URL)))
536 .putString(PROXY, importPreferencesCursor.getString(importPreferencesCursor.getColumnIndexOrThrow(PROXY)))
537 .putString(PROXY_CUSTOM_URL, importPreferencesCursor.getString(importPreferencesCursor.getColumnIndexOrThrow(PROXY_CUSTOM_URL)))
538 .putBoolean(FULL_SCREEN_BROWSING_MODE, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(FULL_SCREEN_BROWSING_MODE)) == 1)
539 .putBoolean(HIDE_APP_BAR, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(HIDE_APP_BAR)) == 1)
540 .putBoolean(CLEAR_EVERYTHING, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(CLEAR_EVERYTHING)) == 1)
541 .putBoolean(CLEAR_COOKIES, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(CLEAR_COOKIES)) == 1)
542 .putBoolean(CLEAR_DOM_STORAGE, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(CLEAR_DOM_STORAGE)) == 1)
543 .putBoolean(CLEAR_FORM_DATA, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(CLEAR_FORM_DATA)) == 1) // Clear form data can be removed once the minimum API >= 26.
544 .putBoolean(CLEAR_LOGCAT, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(CLEAR_LOGCAT)) == 1)
545 .putBoolean(CLEAR_CACHE, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(CLEAR_CACHE)) == 1)
546 .putString(HOMEPAGE, importPreferencesCursor.getString(importPreferencesCursor.getColumnIndexOrThrow(HOMEPAGE)))
547 .putString(FONT_SIZE, importPreferencesCursor.getString(importPreferencesCursor.getColumnIndexOrThrow(FONT_SIZE)))
548 .putBoolean(OPEN_INTENTS_IN_NEW_TAB, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(OPEN_INTENTS_IN_NEW_TAB)) == 1)
549 .putBoolean(SWIPE_TO_REFRESH, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(SWIPE_TO_REFRESH)) == 1)
550 .putBoolean(DOWNLOAD_WITH_EXTERNAL_APP, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(DOWNLOAD_WITH_EXTERNAL_APP)) == 1)
551 .putBoolean(SCROLL_APP_BAR, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(SCROLL_APP_BAR)) == 1)
552 .putBoolean(BOTTOM_APP_BAR, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(BOTTOM_APP_BAR)) == 1)
553 .putBoolean(DISPLAY_ADDITIONAL_APP_BAR_ICONS, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(DISPLAY_ADDITIONAL_APP_BAR_ICONS)) == 1)
554 .putString(APP_THEME, importPreferencesCursor.getString(importPreferencesCursor.getColumnIndexOrThrow(APP_THEME)))
555 .putString(WEBVIEW_THEME, importPreferencesCursor.getString(importPreferencesCursor.getColumnIndexOrThrow(WEBVIEW_THEME)))
556 .putBoolean(WIDE_VIEWPORT, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(WIDE_VIEWPORT)) == 1)
557 .putBoolean(DISPLAY_WEBPAGE_IMAGES, importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndexOrThrow(DISPLAY_WEBPAGE_IMAGES)) == 1)
560 // Close the preferences cursor and database.
561 importPreferencesCursor.close()
562 importDatabase.close()
564 // Delete the temporary import file database, journal, and other related auxiliary files.
565 SQLiteDatabase.deleteDatabase(temporaryImportFile)
567 // Return the import successful string.
569 } catch (exception: Exception) {
570 // Return the import error.
575 fun exportUnencrypted(exportFileOutputStream: OutputStream, context: Context): String {
577 // Create a temporary export file.
578 val temporaryExportFile = File.createTempFile("temporary_export_file", null, context.cacheDir)
580 // Create the temporary export database.
581 val temporaryExportDatabase = SQLiteDatabase.openOrCreateDatabase(temporaryExportFile, null)
583 // Set the temporary export database version number.
584 temporaryExportDatabase.version = SCHEMA_VERSION
587 // Create the temporary export database bookmarks table.
588 temporaryExportDatabase.execSQL(BookmarksDatabaseHelper.CREATE_BOOKMARKS_TABLE)
590 // Open the bookmarks database.
591 val bookmarksDatabaseHelper = BookmarksDatabaseHelper(context)
593 // Get a full bookmarks cursor.
594 val bookmarksCursor = bookmarksDatabaseHelper.allBookmarks
596 // Move to the first record.
597 bookmarksCursor.moveToFirst()
599 // Copy the data from the bookmarks cursor into the export database.
600 for (i in 0 until bookmarksCursor.count) {
601 // Create a bookmark content values.
602 val bookmarkContentValues = ContentValues()
604 // Populate the bookmark content values.
605 bookmarkContentValues.put(BookmarksDatabaseHelper.BOOKMARK_NAME, bookmarksCursor.getString(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_NAME)))
606 bookmarkContentValues.put(BookmarksDatabaseHelper.BOOKMARK_URL, bookmarksCursor.getString(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_URL)))
607 bookmarkContentValues.put(BookmarksDatabaseHelper.PARENT_FOLDER, bookmarksCursor.getString(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.PARENT_FOLDER)))
608 bookmarkContentValues.put(BookmarksDatabaseHelper.DISPLAY_ORDER, bookmarksCursor.getInt(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.DISPLAY_ORDER)))
609 bookmarkContentValues.put(BookmarksDatabaseHelper.IS_FOLDER, bookmarksCursor.getInt(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.IS_FOLDER)))
610 bookmarkContentValues.put(BookmarksDatabaseHelper.FAVORITE_ICON, bookmarksCursor.getBlob(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.FAVORITE_ICON)))
612 // Insert the content values into the temporary export database.
613 temporaryExportDatabase.insert(BookmarksDatabaseHelper.BOOKMARKS_TABLE, null, bookmarkContentValues)
615 // Advance to the next record.
616 bookmarksCursor.moveToNext()
619 // Close the bookmarks cursor and database.
620 bookmarksCursor.close()
621 bookmarksDatabaseHelper.close()
624 // Create the temporary export database domains table.
625 temporaryExportDatabase.execSQL(DomainsDatabaseHelper.CREATE_DOMAINS_TABLE)
627 // Open the domains database.
628 val domainsDatabaseHelper = DomainsDatabaseHelper(context)
630 // Get a full domains database cursor.
631 val domainsCursor = domainsDatabaseHelper.completeCursorOrderedByDomain
633 // Move to the first record.
634 domainsCursor.moveToFirst()
636 // Copy the data from the domains cursor into the export database.
637 for (i in 0 until domainsCursor.count) {
638 // Create a domain content values.
639 val domainContentValues = ContentValues()
641 // Populate the domain content values.
642 domainContentValues.put(DomainsDatabaseHelper.DOMAIN_NAME, domainsCursor.getString(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DOMAIN_NAME)))
643 domainContentValues.put(DomainsDatabaseHelper.ENABLE_JAVASCRIPT, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_JAVASCRIPT)))
644 domainContentValues.put(DomainsDatabaseHelper.COOKIES, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.COOKIES)))
645 domainContentValues.put(DomainsDatabaseHelper.ENABLE_DOM_STORAGE, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_DOM_STORAGE)))
646 domainContentValues.put(DomainsDatabaseHelper.ENABLE_FORM_DATA, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FORM_DATA)))
647 domainContentValues.put(DomainsDatabaseHelper.ENABLE_EASYLIST, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_EASYLIST)))
648 domainContentValues.put(DomainsDatabaseHelper.ENABLE_EASYPRIVACY, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_EASYPRIVACY)))
649 domainContentValues.put(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST)))
650 domainContentValues.put(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST,
651 domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST)))
652 domainContentValues.put(DomainsDatabaseHelper.ULTRALIST, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ULTRALIST)))
653 domainContentValues.put(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY)))
654 domainContentValues.put(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS)))
655 domainContentValues.put(DomainsDatabaseHelper.USER_AGENT, domainsCursor.getString(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.USER_AGENT)))
656 domainContentValues.put(DomainsDatabaseHelper.FONT_SIZE, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.FONT_SIZE)))
657 domainContentValues.put(DomainsDatabaseHelper.SWIPE_TO_REFRESH, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SWIPE_TO_REFRESH)))
658 domainContentValues.put(DomainsDatabaseHelper.WEBVIEW_THEME, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.WEBVIEW_THEME)))
659 domainContentValues.put(DomainsDatabaseHelper.WIDE_VIEWPORT, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.WIDE_VIEWPORT)))
660 domainContentValues.put(DomainsDatabaseHelper.DISPLAY_IMAGES, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DISPLAY_IMAGES)))
661 domainContentValues.put(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE)))
662 domainContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME, domainsCursor.getString(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME)))
663 domainContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION, domainsCursor.getString(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION)))
664 domainContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT,
665 domainsCursor.getString(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT)))
666 domainContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME, domainsCursor.getString(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME)))
667 domainContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION, domainsCursor.getString(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION)))
668 domainContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT,
669 domainsCursor.getString(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT)))
670 domainContentValues.put(DomainsDatabaseHelper.SSL_START_DATE, domainsCursor.getLong(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_START_DATE)))
671 domainContentValues.put(DomainsDatabaseHelper.SSL_END_DATE, domainsCursor.getLong(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.SSL_END_DATE)))
672 domainContentValues.put(DomainsDatabaseHelper.PINNED_IP_ADDRESSES, domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.PINNED_IP_ADDRESSES)))
673 domainContentValues.put(DomainsDatabaseHelper.IP_ADDRESSES, domainsCursor.getString(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.IP_ADDRESSES)))
675 // Insert the content values into the temporary export database.
676 temporaryExportDatabase.insert(DomainsDatabaseHelper.DOMAINS_TABLE, null, domainContentValues)
678 // Advance to the next record.
679 domainsCursor.moveToNext()
682 // Close the domains cursor and database.
683 domainsCursor.close()
684 domainsDatabaseHelper.close()
687 // Prepare the preferences table SQL creation string.
688 val createPreferencesTable = "CREATE TABLE " + PREFERENCES_TABLE + " (" +
689 ID + " INTEGER PRIMARY KEY, " +
690 JAVASCRIPT + " BOOLEAN, " +
691 COOKIES + " BOOLEAN, " +
692 DOM_STORAGE + " BOOLEAN, " +
693 SAVE_FORM_DATA + " BOOLEAN, " +
694 USER_AGENT + " TEXT, " +
695 CUSTOM_USER_AGENT + " TEXT, " +
696 INCOGNITO_MODE + " BOOLEAN, " +
697 ALLOW_SCREENSHOTS + " BOOLEAN, " +
698 EASYLIST + " BOOLEAN, " +
699 EASYPRIVACY + " BOOLEAN, " +
700 FANBOYS_ANNOYANCE_LIST + " BOOLEAN, " +
701 FANBOYS_SOCIAL_BLOCKING_LIST + " BOOLEAN, " +
702 ULTRALIST + " BOOLEAN, " +
703 ULTRAPRIVACY + " BOOLEAN, " +
704 BLOCK_ALL_THIRD_PARTY_REQUESTS + " BOOLEAN, " +
705 GOOGLE_ANALYTICS + " BOOLEAN, " +
706 FACEBOOK_CLICK_IDS + " BOOLEAN, " +
707 TWITTER_AMP_REDIRECTS + " BOOLEAN, " +
709 SEARCH_CUSTOM_URL + " TEXT, " +
711 PROXY_CUSTOM_URL + " TEXT, " +
712 FULL_SCREEN_BROWSING_MODE + " BOOLEAN, " +
713 HIDE_APP_BAR + " BOOLEAN, " +
714 CLEAR_EVERYTHING + " BOOLEAN, " +
715 CLEAR_COOKIES + " BOOLEAN, " +
716 CLEAR_DOM_STORAGE + " BOOLEAN, " +
717 CLEAR_FORM_DATA + " BOOLEAN, " +
718 CLEAR_LOGCAT + " BOOLEAN, " +
719 CLEAR_CACHE + " BOOLEAN, " +
720 HOMEPAGE + " TEXT, " +
721 FONT_SIZE + " TEXT, " +
722 OPEN_INTENTS_IN_NEW_TAB + " BOOLEAN, " +
723 SWIPE_TO_REFRESH + " BOOLEAN, " +
724 DOWNLOAD_WITH_EXTERNAL_APP + " BOOLEAN, " +
725 SCROLL_APP_BAR + " BOOLEAN, " +
726 BOTTOM_APP_BAR + " BOOLEAN, " +
727 DISPLAY_ADDITIONAL_APP_BAR_ICONS + " BOOLEAN, " +
728 APP_THEME + " TEXT, " +
729 WEBVIEW_THEME + " TEXT, " +
730 WIDE_VIEWPORT + " BOOLEAN, " +
731 DISPLAY_WEBPAGE_IMAGES + " BOOLEAN)"
733 // Create the temporary export database preferences table.
734 temporaryExportDatabase.execSQL(createPreferencesTable)
736 // Get a handle for the shared preference.
737 val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
739 // Create a preferences content values.
740 val preferencesContentValues = ContentValues()
742 // Populate the preferences content values.
743 preferencesContentValues.put(JAVASCRIPT, sharedPreferences.getBoolean(JAVASCRIPT, false))
744 preferencesContentValues.put(COOKIES, sharedPreferences.getBoolean(COOKIES, false))
745 preferencesContentValues.put(DOM_STORAGE, sharedPreferences.getBoolean(DOM_STORAGE, false))
746 preferencesContentValues.put(SAVE_FORM_DATA, sharedPreferences.getBoolean(SAVE_FORM_DATA, false)) // Save form data can be removed once the minimum API >= 26.
747 preferencesContentValues.put(USER_AGENT, sharedPreferences.getString(USER_AGENT, context.getString(R.string.user_agent_default_value)))
748 preferencesContentValues.put(CUSTOM_USER_AGENT, sharedPreferences.getString(CUSTOM_USER_AGENT, context.getString(R.string.custom_user_agent_default_value)))
749 preferencesContentValues.put(INCOGNITO_MODE, sharedPreferences.getBoolean(INCOGNITO_MODE, false))
750 preferencesContentValues.put(ALLOW_SCREENSHOTS, sharedPreferences.getBoolean(ALLOW_SCREENSHOTS, false))
751 preferencesContentValues.put(EASYLIST, sharedPreferences.getBoolean(EASYLIST, true))
752 preferencesContentValues.put(EASYPRIVACY, sharedPreferences.getBoolean(EASYPRIVACY, true))
753 preferencesContentValues.put(FANBOYS_ANNOYANCE_LIST, sharedPreferences.getBoolean(FANBOYS_ANNOYANCE_LIST, true))
754 preferencesContentValues.put(FANBOYS_SOCIAL_BLOCKING_LIST, sharedPreferences.getBoolean(FANBOYS_SOCIAL_BLOCKING_LIST, true))
755 preferencesContentValues.put(ULTRALIST, sharedPreferences.getBoolean(ULTRALIST, true))
756 preferencesContentValues.put(ULTRAPRIVACY, sharedPreferences.getBoolean(ULTRAPRIVACY, true))
757 preferencesContentValues.put(BLOCK_ALL_THIRD_PARTY_REQUESTS, sharedPreferences.getBoolean(BLOCK_ALL_THIRD_PARTY_REQUESTS, false))
758 preferencesContentValues.put(GOOGLE_ANALYTICS, sharedPreferences.getBoolean(GOOGLE_ANALYTICS, true))
759 preferencesContentValues.put(FACEBOOK_CLICK_IDS, sharedPreferences.getBoolean(FACEBOOK_CLICK_IDS, true))
760 preferencesContentValues.put(TWITTER_AMP_REDIRECTS, sharedPreferences.getBoolean(TWITTER_AMP_REDIRECTS, true))
761 preferencesContentValues.put(SEARCH, sharedPreferences.getString(SEARCH, context.getString(R.string.search_default_value)))
762 preferencesContentValues.put(SEARCH_CUSTOM_URL, sharedPreferences.getString(SEARCH_CUSTOM_URL, context.getString(R.string.search_custom_url_default_value)))
763 preferencesContentValues.put(PROXY, sharedPreferences.getString(PROXY, context.getString(R.string.proxy_default_value)))
764 preferencesContentValues.put(PROXY_CUSTOM_URL, sharedPreferences.getString(PROXY_CUSTOM_URL, context.getString(R.string.proxy_custom_url_default_value)))
765 preferencesContentValues.put(FULL_SCREEN_BROWSING_MODE, sharedPreferences.getBoolean(FULL_SCREEN_BROWSING_MODE, false))
766 preferencesContentValues.put(HIDE_APP_BAR, sharedPreferences.getBoolean(HIDE_APP_BAR, true))
767 preferencesContentValues.put(CLEAR_EVERYTHING, sharedPreferences.getBoolean(CLEAR_EVERYTHING, true))
768 preferencesContentValues.put(CLEAR_COOKIES, sharedPreferences.getBoolean(CLEAR_COOKIES, true))
769 preferencesContentValues.put(CLEAR_DOM_STORAGE, sharedPreferences.getBoolean(CLEAR_DOM_STORAGE, true))
770 preferencesContentValues.put(CLEAR_FORM_DATA, sharedPreferences.getBoolean(CLEAR_FORM_DATA, true)) // Clear form data can be removed once the minimum API >= 26.
771 preferencesContentValues.put(CLEAR_LOGCAT, sharedPreferences.getBoolean(CLEAR_LOGCAT, true))
772 preferencesContentValues.put(CLEAR_CACHE, sharedPreferences.getBoolean(CLEAR_CACHE, true))
773 preferencesContentValues.put(HOMEPAGE, sharedPreferences.getString(HOMEPAGE, context.getString(R.string.homepage_default_value)))
774 preferencesContentValues.put(FONT_SIZE, sharedPreferences.getString(FONT_SIZE, context.getString(R.string.font_size_default_value)))
775 preferencesContentValues.put(OPEN_INTENTS_IN_NEW_TAB, sharedPreferences.getBoolean(OPEN_INTENTS_IN_NEW_TAB, true))
776 preferencesContentValues.put(SWIPE_TO_REFRESH, sharedPreferences.getBoolean(SWIPE_TO_REFRESH, true))
777 preferencesContentValues.put(DOWNLOAD_WITH_EXTERNAL_APP, sharedPreferences.getBoolean(DOWNLOAD_WITH_EXTERNAL_APP, false))
778 preferencesContentValues.put(SCROLL_APP_BAR, sharedPreferences.getBoolean(SCROLL_APP_BAR, true))
779 preferencesContentValues.put(BOTTOM_APP_BAR, sharedPreferences.getBoolean(BOTTOM_APP_BAR, false))
780 preferencesContentValues.put(DISPLAY_ADDITIONAL_APP_BAR_ICONS, sharedPreferences.getBoolean(DISPLAY_ADDITIONAL_APP_BAR_ICONS, false))
781 preferencesContentValues.put(APP_THEME, sharedPreferences.getString(APP_THEME, context.getString(R.string.app_theme_default_value)))
782 preferencesContentValues.put(WEBVIEW_THEME, sharedPreferences.getString(WEBVIEW_THEME, context.getString(R.string.webview_theme_default_value)))
783 preferencesContentValues.put(WIDE_VIEWPORT, sharedPreferences.getBoolean(WIDE_VIEWPORT, true))
784 preferencesContentValues.put(DISPLAY_WEBPAGE_IMAGES, sharedPreferences.getBoolean(DISPLAY_WEBPAGE_IMAGES, true))
786 // Insert the preferences content values into the temporary export database.
787 temporaryExportDatabase.insert(PREFERENCES_TABLE, null, preferencesContentValues)
789 // Close the temporary export database.
790 temporaryExportDatabase.close()
793 // The file may be copied directly in Kotlin using `File.copyTo`. <https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/java.io.-file/copy-to.html>
794 // It can be copied in Android using `Files.copy` once the minimum API >= 26.
795 // <https://developer.android.com/reference/java/nio/file/Files#copy(java.nio.file.Path,%20java.nio.file.Path,%20java.nio.file.CopyOption...)>
796 // However, the file cannot be acquired from the content URI until the minimum API >= 29. <https://developer.android.com/reference/kotlin/android/content/ContentResolver#openfile>
798 // Create the temporary export file input stream.
799 val temporaryExportFileInputStream = FileInputStream(temporaryExportFile)
801 // Create a byte array.
802 val transferByteArray = ByteArray(1024)
804 // Create an integer to track the number of bytes read.
807 // Copy the temporary export file to the export file output stream.
808 while (temporaryExportFileInputStream.read(transferByteArray).also { bytesRead = it } > 0) {
809 exportFileOutputStream.write(transferByteArray, 0, bytesRead)
812 // Flush the export file output stream.
813 exportFileOutputStream.flush()
815 // Close the file streams.
816 temporaryExportFileInputStream.close()
817 exportFileOutputStream.close()
819 // Delete the temporary export file database, journal, and other related auxiliary files.
820 SQLiteDatabase.deleteDatabase(temporaryExportFile)
822 // Return the export successful string.
824 } catch (exception: Exception) {
825 // Return the export error.