]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blob - app/src/main/java/com/stoutner/privacybrowser/helpers/DomainsDatabaseHelper.kt
dea4c75fe5316040456858d65defca7012e11b67
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / helpers / DomainsDatabaseHelper.kt
1 /*
2  * Copyright 2017-2024 Soren Stoutner <soren@stoutner.com>.
3  *
4  * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
5  *
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.
10  *
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.
15  *
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/>.
18  */
19
20 package com.stoutner.privacybrowser.helpers
21
22 import android.content.ContentValues
23 import android.content.Context
24 import android.database.Cursor
25 import android.database.DatabaseUtils
26 import android.database.sqlite.SQLiteDatabase
27 import android.database.sqlite.SQLiteOpenHelper
28
29 import androidx.preference.PreferenceManager
30
31 import com.stoutner.privacybrowser.R
32
33 // Define the class constants.
34 private const val SCHEMA_VERSION = 16
35
36 // Define the public database constants.
37 const val DOMAINS_DATABASE = "domains.db"
38 const val DOMAINS_TABLE = "domains"
39
40 // Define the public spinner constants.
41 const val SYSTEM_DEFAULT = 0
42 const val ENABLED = 1
43 const val DISABLED = 2
44 const val LIGHT_THEME = 1
45 const val DARK_THEME = 2
46
47 // Define the public schema constants.
48 const val BLOCK_ALL_THIRD_PARTY_REQUESTS = "blockallthirdpartyrequests"
49 const val COOKIES = "cookies"
50 const val DISPLAY_IMAGES = "displayimages"
51 const val DOMAIN_NAME = "domainname"
52 const val ENABLE_DOM_STORAGE = "enabledomstorage"
53 const val ENABLE_EASYLIST = "enableeasylist"
54 const val ENABLE_EASYPRIVACY = "enableeasyprivacy"
55 const val ENABLE_FANBOYS_ANNOYANCE_LIST = "enablefanboysannoyancelist"
56 const val ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST = "enablefanboyssocialblockinglist"
57 const val ENABLE_FORM_DATA = "enableformdata" // Form data can be removed once the minimum API >= 26.
58 const val ENABLE_JAVASCRIPT = "enablejavascript"
59 const val ENABLE_ULTRAPRIVACY = "enableultraprivacy"
60 const val FONT_SIZE = "fontsize"
61 const val ID = "_id"
62 const val IP_ADDRESSES = "ip_addresses"
63 const val PINNED_IP_ADDRESSES = "pinned_ip_addresses"
64 const val PINNED_SSL_CERTIFICATE = "pinnedsslcertificate"
65 const val SSL_END_DATE = "sslenddate"
66 const val SSL_START_DATE = "sslstartdate"
67 const val SSL_ISSUED_BY_COMMON_NAME = "sslissuedbycommonname"
68 const val SSL_ISSUED_BY_ORGANIZATION = "sslissuedbyorganization"
69 const val SSL_ISSUED_BY_ORGANIZATIONAL_UNIT = "sslissuedbyorganizationalunit"
70 const val SSL_ISSUED_TO_COMMON_NAME = "sslissuedtocommonname"
71 const val SSL_ISSUED_TO_ORGANIZATION = "sslissuedtoorganization"
72 const val SSL_ISSUED_TO_ORGANIZATIONAL_UNIT = "sslissuedtoorganizationalunit"
73 const val SWIPE_TO_REFRESH = "swipetorefresh"
74 const val ULTRALIST = "ultralist"
75 const val USER_AGENT = "useragent"
76 const val WEBVIEW_THEME = "webview_theme"
77 const val WIDE_VIEWPORT = "wide_viewport"
78
79 // Define the public table creation constant.
80 const val CREATE_DOMAINS_TABLE = "CREATE TABLE $DOMAINS_TABLE (" +
81         "$ID INTEGER PRIMARY KEY, " +
82         "$DOMAIN_NAME TEXT, " +
83         "$ENABLE_JAVASCRIPT INTEGER, " +
84         "$COOKIES INTEGER, " +
85         "$ENABLE_DOM_STORAGE INTEGER, " +
86         "$ENABLE_FORM_DATA INTEGER, " +
87         "$USER_AGENT TEXT, " +
88         "$ENABLE_EASYLIST INTEGER, " +
89         "$ENABLE_EASYPRIVACY INTEGER, " +
90         "$ENABLE_FANBOYS_ANNOYANCE_LIST INTEGER, " +
91         "$ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST INTEGER, " +
92         "$ULTRALIST INTEGER, " +
93         "$ENABLE_ULTRAPRIVACY INTEGER, " +
94         "$BLOCK_ALL_THIRD_PARTY_REQUESTS INTEGER, " +
95         "$FONT_SIZE INTEGER, " +
96         "$SWIPE_TO_REFRESH INTEGER, " +
97         "$WEBVIEW_THEME INTEGER, " +
98         "$WIDE_VIEWPORT INTEGER, " +
99         "$DISPLAY_IMAGES INTEGER, " +
100         "$PINNED_SSL_CERTIFICATE BOOLEAN, " +
101         "$SSL_ISSUED_TO_COMMON_NAME TEXT, " +
102         "$SSL_ISSUED_TO_ORGANIZATION TEXT, " +
103         "$SSL_ISSUED_TO_ORGANIZATIONAL_UNIT TEXT, " +
104         "$SSL_ISSUED_BY_COMMON_NAME TEXT, " +
105         "$SSL_ISSUED_BY_ORGANIZATION TEXT, " +
106         "$SSL_ISSUED_BY_ORGANIZATIONAL_UNIT TEXT, " +
107         "$SSL_START_DATE INTEGER, " +
108         "$SSL_END_DATE INTEGER, " +
109         "$PINNED_IP_ADDRESSES BOOLEAN, " +
110         "$IP_ADDRESSES TEXT)"
111
112 class DomainsDatabaseHelper(private val appContext: Context) : SQLiteOpenHelper(appContext, DOMAINS_DATABASE, null, SCHEMA_VERSION) {
113     override fun onCreate(domainsDatabase: SQLiteDatabase) {
114         // Create the domains table.
115         domainsDatabase.execSQL(CREATE_DOMAINS_TABLE)
116     }
117
118     override fun onUpgrade(domainsDatabase: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
119         // Get a handle for the shared preference.
120         val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(appContext)
121
122         // Upgrade from schema version 1, first used in Privacy Browser 2.0, to schema version 2, first used in Privacy Browser 2.3.
123         if (oldVersion < 2) {
124             // Add the display images column.
125             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $DISPLAY_IMAGES INTEGER")
126         }
127
128         // Upgrade from schema version 2, first used in Privacy Browser 2.3, to schema version 3, first used in Privacy Browser 2.5.
129         if (oldVersion < 3) {
130             //  Add the SSL certificate columns.
131             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $PINNED_SSL_CERTIFICATE BOOLEAN")
132             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $SSL_ISSUED_TO_COMMON_NAME TEXT")
133             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $SSL_ISSUED_TO_ORGANIZATION TEXT")
134             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $SSL_ISSUED_TO_ORGANIZATIONAL_UNIT TEXT")
135             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $SSL_ISSUED_BY_COMMON_NAME TEXT")
136             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $SSL_ISSUED_BY_ORGANIZATION TEXT")
137             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $SSL_ISSUED_BY_ORGANIZATIONAL_UNIT TEXT")
138             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $SSL_START_DATE INTEGER")
139             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $SSL_END_DATE INTEGER")
140         }
141
142         // Upgrade from schema version 3, first used in Privacy Browser 2.5, to schema version 4, first used in Privacy Browser 2.6.
143         if (oldVersion < 4) {
144             // Add the night mode column.
145             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN nightmode INTEGER")
146         }
147
148         // Upgrade from schema version 4, first used in Privacy Browser 2.6, to schema version 5, first used in Privacy Browser 2.9.
149         if (oldVersion < 5) {
150             // Add the block lists columns.
151             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $ENABLE_EASYLIST BOOLEAN")
152             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $ENABLE_EASYPRIVACY BOOLEAN")
153             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $ENABLE_FANBOYS_ANNOYANCE_LIST BOOLEAN")
154             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST BOOLEAN")
155
156             // Get the default filter list settings.
157             val easyListEnabled = sharedPreferences.getBoolean(appContext.getString(R.string.easylist_key), true)
158             val easyPrivacyEnabled = sharedPreferences.getBoolean(appContext.getString(R.string.easyprivacy_key), true)
159             val fanboyAnnoyanceListEnabled = sharedPreferences.getBoolean(appContext.getString(R.string.fanboys_annoyance_list_key), true)
160             val fanboySocialBlockingListEnabled = sharedPreferences.getBoolean(appContext.getString(R.string.fanboys_social_blocking_list_key), true)
161
162             // Set EasyList for existing rows according to the current system-wide default.
163             // This can switch to using the variables directly once the API >= 30.  <https://www.sqlite.org/datatype3.html#boolean_datatype>
164             // <https://developer.android.com/reference/android/database/sqlite/package-summary>
165             if (easyListEnabled) {
166                 domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ENABLE_EASYLIST = 1")
167             } else {
168                 domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ENABLE_EASYLIST = 0")
169             }
170
171             // Set EasyPrivacy for existing rows according to the current system-wide default.
172             if (easyPrivacyEnabled) {
173                 domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ENABLE_EASYPRIVACY = 1")
174             } else {
175                 domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ENABLE_EASYPRIVACY = 0")
176             }
177
178             // Set Fanboy's Annoyance List for existing rows according to the current system-wide default.
179             if (fanboyAnnoyanceListEnabled) {
180                 domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ENABLE_FANBOYS_ANNOYANCE_LIST = 1")
181             } else {
182                 domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ENABLE_FANBOYS_ANNOYANCE_LIST = 0")
183             }
184
185             // Set Fanboy's Social Blocking List for existing rows according to the current system-wide default.
186             if (fanboySocialBlockingListEnabled) {
187                 domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST = 1")
188             } else {
189                 domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST = 0")
190             }
191         }
192
193         // Upgrade from schema version 5, first used in Privacy Browser 2.9, to schema version 6, first used in Privacy Browser 2.11.
194         if (oldVersion < 6) {
195             // 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.
196             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $SWIPE_TO_REFRESH INTEGER")
197         }
198
199         // Upgrade from schema version 6, first used in Privacy Browser 2.11, to schema version 7, first used in Privacy Browser 2.12.
200         if (oldVersion < 7) {
201             // 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.
202             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $BLOCK_ALL_THIRD_PARTY_REQUESTS BOOLEAN")
203         }
204
205         // Upgrade from schema version 7, first used in Privacy Browser 2.12, to schema version 8, first used in Privacy Browser 2.12.
206         // For some reason (lack of planning or attention to detail), the 2.12 update included two schema version jumps.
207         if (oldVersion < 8) {
208             // Add the UltraPrivacy column.
209             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $ENABLE_ULTRAPRIVACY BOOLEAN")
210
211             // Enable it for all existing rows.
212             domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ENABLE_ULTRAPRIVACY = 1")
213         }
214
215         // Upgrade from schema version 8, first used in Privacy Browser 2.12, to schema version 9, first used in Privacy Browser 2.16.
216         if (oldVersion < 9) {
217             // Add the pinned IP addresses columns.  These default to `0` and `""`, so a separate step isn't needed to populate the columns.
218             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $PINNED_IP_ADDRESSES BOOLEAN")
219             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $IP_ADDRESSES TEXT")
220         }
221
222         // Upgrade from schema version 9, first used in Privacy Browser 2.16, to schema version 10, first used in Privacy Browser 3.1.
223         if (oldVersion < 10) {
224             // Add the wide viewport column.  This defaults to `0`, which is `System default`, so a separate step isn't needed to populate the column.
225             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $WIDE_VIEWPORT INTEGER")
226         }
227
228         // Upgrade from schema version 10, first used in Privacy Browser 3.1, to schema version 11, first used in Privacy Browser 3.2.
229         if (oldVersion < 11) {
230             // Add the UltraList column.
231             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $ULTRALIST BOOLEAN")
232
233             // Enable it for all existing rows.
234             domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ULTRALIST = 1")
235         }
236
237         // Upgrade from schema version 11, first used in Privacy Browser 3.2, to schema version 12, first used in Privacy Browser 3.5.
238         if (oldVersion < 12) {
239             // Add the WebView theme column.  This defaults to `0`, which is `System default`, so a separate step isn't needed to populate the column.
240             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $WEBVIEW_THEME INTEGER")
241
242             // `night_mode` was removed.
243             // SQLite amazingly only added a command to drop a column in version 3.35.0.  <https://www.sqlite.org/changes.html>
244             // It will be a while before that is supported in Android.  <https://developer.android.com/reference/android/database/sqlite/package-summary>
245             // 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.
246         }
247
248         // Upgrade from schema version 12, first used in Privacy Browser 3.5, to schema version 13, first used in Privacy Browser 3.8.
249         if (oldVersion < 13) {
250             // Add the cookies column.
251             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $COOKIES BOOLEAN")
252
253             // Copy the data from the old column to the new one.
254             domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $COOKIES = enablefirstpartycookies")
255         }
256
257         // Upgrade from schema version 13, first used in Privacy Browser 3.8, to schema version 14, first used in Privacy Browser 3.11.
258         // This upgrade used to add the X-Requested-With header, but that was removed in schema version 15.
259
260         // Upgrade from schema version 14, first used in Privacy Browser 3.11, to schema version 15, first used in Privacy Browser 3.12.
261         // This upgrade removed `x_requested_with_header`.
262         // SQLite amazingly only added a command to drop a column in version 3.35.0.  <https://www.sqlite.org/changes.html>
263         // It will be a while before that is supported in Android.  <https://developer.android.com/reference/android/database/sqlite/package-summary>
264         // Although a new table could be created and all the data copied to it, I think I will just leave the X-Requested-With column.  It will be wiped out the next time an import is run.
265
266         // Upgrade from schema version 15, first used in Privacy Browser 3.12, to schema version 16, first used in Privacy Browser 3.15.
267         if (oldVersion < 16) {
268             // Get the current switch default values.
269             val javaScriptDefaultValue = sharedPreferences.getBoolean(appContext.getString(R.string.javascript_key), false)
270             val cookiesDefaultValue = sharedPreferences.getBoolean(appContext.getString(R.string.cookies_key), false)
271             val domStorageDefaultValue = sharedPreferences.getBoolean(appContext.getString(R.string.dom_storage_key), false)
272             val formDataDefaultValue = sharedPreferences.getBoolean(appContext.getString(R.string.save_form_data_key), false)
273             val easyListDefaultValue = sharedPreferences.getBoolean(appContext.getString(R.string.easylist_key), true)
274             val easyPrivacyDefaultValue = sharedPreferences.getBoolean(appContext.getString(R.string.easyprivacy_key), true)
275             val fanboysAnnoyanceListDefaultValue = sharedPreferences.getBoolean(appContext.getString(R.string.fanboys_annoyance_list_key), true)
276             val fanboysSocialBlockingListDefaultValue = sharedPreferences.getBoolean(appContext.getString(R.string.fanboys_social_blocking_list), true)
277             val ultraListDefaultValue = sharedPreferences.getBoolean(appContext.getString(R.string.ultralist_key), true)
278             val ultraPrivacyDefaultValue = sharedPreferences.getBoolean(appContext.getString(R.string.ultraprivacy_key), true)
279             val blockAllThirdPartyRequestsDefaultValue = sharedPreferences.getBoolean(appContext.getString(R.string.block_all_third_party_requests_key), false)
280
281             // Get a domains cursor.
282             val domainsCursor = domainsDatabase.rawQuery("SELECT * FROM $DOMAINS_TABLE", null)
283
284             // Get the domains column indexes.
285             val javaScriptColumnIndex = domainsCursor.getColumnIndexOrThrow(ENABLE_JAVASCRIPT)
286             val cookiesColumnIndex = domainsCursor.getColumnIndexOrThrow(COOKIES)
287             val domStorageColumnIndex = domainsCursor.getColumnIndexOrThrow(ENABLE_DOM_STORAGE)
288             val formDataColumnIndex = domainsCursor.getColumnIndexOrThrow(ENABLE_FORM_DATA)
289             val easyListColumnIndex = domainsCursor.getColumnIndexOrThrow(ENABLE_EASYLIST)
290             val easyPrivacyColumnIndex = domainsCursor.getColumnIndexOrThrow(ENABLE_EASYPRIVACY)
291             val fanboysAnnoyanceListColumnIndex = domainsCursor.getColumnIndexOrThrow(ENABLE_FANBOYS_ANNOYANCE_LIST)
292             val fanboysSocialBlockingListColumnIndex = domainsCursor.getColumnIndexOrThrow(ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST)
293             val ultraListColumnIndex = domainsCursor.getColumnIndexOrThrow(ULTRALIST)
294             val ultraPrivacyColumnIndex = domainsCursor.getColumnIndexOrThrow(ENABLE_ULTRAPRIVACY)
295             val blockAllThirdPartyRequestsColumnIndex = domainsCursor.getColumnIndexOrThrow(BLOCK_ALL_THIRD_PARTY_REQUESTS)
296
297             // Convert the domain from the switch booleans to the spinner integers.
298             for (i in 0 until domainsCursor.count) {
299                 // Move to the current record.
300                 domainsCursor.moveToPosition(i)
301
302                 // Get the domain current values.
303                 val javaScriptDomainCurrentValue = domainsCursor.getInt(javaScriptColumnIndex)
304                 val cookiesDomainCurrentValue = domainsCursor.getInt(cookiesColumnIndex)
305                 val domStorageDomainCurrentValue = domainsCursor.getInt(domStorageColumnIndex)
306                 val formDataDomainCurrentValue = domainsCursor.getInt(formDataColumnIndex)
307                 val easyListDomainCurrentValue = domainsCursor.getInt(easyListColumnIndex)
308                 val easyPrivacyDomainCurrentValue = domainsCursor.getInt(easyPrivacyColumnIndex)
309                 val fanboysAnnoyanceListCurrentValue = domainsCursor.getInt(fanboysAnnoyanceListColumnIndex)
310                 val fanboysSocialBlockingListCurrentValue = domainsCursor.getInt(fanboysSocialBlockingListColumnIndex)
311                 val ultraListCurrentValue = domainsCursor.getInt(ultraListColumnIndex)
312                 val ultraPrivacyCurrentValue = domainsCursor.getInt(ultraPrivacyColumnIndex)
313                 val blockAllThirdPartyRequestsCurrentValue = domainsCursor.getInt(blockAllThirdPartyRequestsColumnIndex)
314
315                 // Instantiate a domain content values.
316                 val domainContentValues = ContentValues()
317
318                 // Populate the domain content values.
319                 domainContentValues.put(ENABLE_JAVASCRIPT, convertFromSwitchToSpinner(javaScriptDefaultValue, javaScriptDomainCurrentValue))
320                 domainContentValues.put(COOKIES, convertFromSwitchToSpinner(cookiesDefaultValue, cookiesDomainCurrentValue))
321                 domainContentValues.put(ENABLE_DOM_STORAGE, convertFromSwitchToSpinner(domStorageDefaultValue, domStorageDomainCurrentValue))
322                 domainContentValues.put(ENABLE_FORM_DATA, convertFromSwitchToSpinner(formDataDefaultValue, formDataDomainCurrentValue))
323                 domainContentValues.put(ENABLE_EASYLIST, convertFromSwitchToSpinner(easyListDefaultValue, easyListDomainCurrentValue))
324                 domainContentValues.put(ENABLE_EASYPRIVACY, convertFromSwitchToSpinner(easyPrivacyDefaultValue, easyPrivacyDomainCurrentValue))
325                 domainContentValues.put(ENABLE_FANBOYS_ANNOYANCE_LIST, convertFromSwitchToSpinner(fanboysAnnoyanceListDefaultValue, fanboysAnnoyanceListCurrentValue))
326                 domainContentValues.put(ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST, convertFromSwitchToSpinner(fanboysSocialBlockingListDefaultValue, fanboysSocialBlockingListCurrentValue))
327                 domainContentValues.put(ULTRALIST, convertFromSwitchToSpinner(ultraListDefaultValue, ultraListCurrentValue))
328                 domainContentValues.put(ENABLE_ULTRAPRIVACY, convertFromSwitchToSpinner(ultraPrivacyDefaultValue, ultraPrivacyCurrentValue))
329                 domainContentValues.put(BLOCK_ALL_THIRD_PARTY_REQUESTS, convertFromSwitchToSpinner(blockAllThirdPartyRequestsDefaultValue, blockAllThirdPartyRequestsCurrentValue))
330
331                 // Get the current database ID.
332                 val currentDatabaseId = domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(ID))
333
334                 // Update the row for the specified database ID.
335                 domainsDatabase.update(DOMAINS_TABLE, domainContentValues, "$ID = $currentDatabaseId", null)
336             }
337
338             // Close the cursor.
339             domainsCursor.close()
340         }
341     }
342
343     val completeCursorOrderedByDomain: Cursor
344         get() {
345             // Get a readable database handle.
346             val domainsDatabase = this.readableDatabase
347
348             // Return everything in the domains table ordered by the domain name.  The cursor can't be closed because it is needed in the calling activity.
349             return domainsDatabase.rawQuery("SELECT * FROM $DOMAINS_TABLE ORDER BY $DOMAIN_NAME ASC", null)
350         }
351
352     val domainNameCursorOrderedByDomain: Cursor
353         get() {
354             // Get a readable database handle.
355             val domainsDatabase = this.readableDatabase
356
357             // Return the database id and the domain name in the domains table ordered by the domain name.  The cursor can't be closed because it is needed in the calling activity.
358             return domainsDatabase.rawQuery("SELECT $ID, $DOMAIN_NAME FROM $DOMAINS_TABLE ORDER BY $DOMAIN_NAME ASC", null)
359         }
360     
361     // This method is used to convert the old domain settings switches to spinners.
362     private fun convertFromSwitchToSpinner(systemDefault: Boolean, currentDatabaseInteger: Int): Int {
363         // Return the new spinner integer.
364         return if ((!systemDefault && (currentDatabaseInteger == 0)) ||
365                     (systemDefault && (currentDatabaseInteger == 1)))  // The system default is currently selected.
366             SYSTEM_DEFAULT
367         else if (currentDatabaseInteger == 0)  // The switch is currently disabled and that is not the system default.
368             DISABLED
369         else  // The switch is currently enabled and that is not the system default.
370             ENABLED
371     }
372
373     fun getDomainNameCursorOrderedByDomainExcept(databaseId: Int): Cursor {
374         // Get a readable database handle.
375         val domainsDatabase = this.readableDatabase
376
377         // Return a cursor with the database IDs and domain names except for the specified ID ordered by domain name.  The cursor can't be closed because it is needed in the calling activity.
378         return domainsDatabase.rawQuery("SELECT $ID, $DOMAIN_NAME FROM $DOMAINS_TABLE WHERE $ID IS NOT $databaseId ORDER BY $DOMAIN_NAME ASC", null)
379     }
380
381     fun getCursorForId(databaseId: Int): Cursor {
382         // Get a readable database handle.
383         val domainsDatabase = this.readableDatabase
384
385         // Return a cursor for the specified database ID.  The cursor can't be closed because it is needed in the calling activity.
386         return domainsDatabase.rawQuery("SELECT * FROM $DOMAINS_TABLE WHERE $ID = $databaseId", null)
387     }
388
389     fun getCursorForDomainName(domainName: String): Cursor {
390         // Get a readable database handle.
391         val domainsDatabase = this.readableDatabase
392
393         // SQL escape the domain name.
394         val sqlEscapedDomainName = DatabaseUtils.sqlEscapeString(domainName)
395
396         // Return a cursor for the requested domain name.  The cursor can't be closed because it is needed in the calling activity.
397         return domainsDatabase.rawQuery("SELECT * FROM $DOMAINS_TABLE WHERE $DOMAIN_NAME = $sqlEscapedDomainName", null)
398     }
399
400     fun addDomain(contentValues: ContentValues) {
401         // Get a writable database handle.
402         val domainsDatabase = this.writableDatabase
403
404         // Add the new domain.
405         domainsDatabase.insert(DOMAINS_TABLE, null, contentValues)
406
407         // Close the database handle.
408         domainsDatabase.close()
409     }
410
411     fun addDomain(domainName: String): Int {
412         // Add the domain with default settings.
413         return addDomain(domainName, SYSTEM_DEFAULT, SYSTEM_DEFAULT, SYSTEM_DEFAULT, SYSTEM_DEFAULT, appContext.getString(R.string.system_default_user_agent), SYSTEM_DEFAULT, SYSTEM_DEFAULT, SYSTEM_DEFAULT,
414                          SYSTEM_DEFAULT, SYSTEM_DEFAULT, SYSTEM_DEFAULT, SYSTEM_DEFAULT, SYSTEM_DEFAULT, SYSTEM_DEFAULT, SYSTEM_DEFAULT, SYSTEM_DEFAULT, SYSTEM_DEFAULT)
415     }
416
417     fun addDomain(domainName: String, javaScriptInt: Int, cookiesInt: Int, domStorageInt: Int, formDataInt: Int, userAgentName: String, easyListInt: Int, easyPrivacyInt: Int, fanboysAnnoyanceListInt: Int,
418                   fanboysSocialBlockingListInt: Int, ultraListInt: Int, ultraPrivacyInt: Int, blockAllThirdPartyRequestsInt: Int, fontSizeInt: Int, swipeToRefreshInt: Int, webViewThemeInt: Int,
419                   wideViewportInt: Int, displayImagesInt: Int): Int {
420         // Instantiate a content values.
421         val domainContentValues = ContentValues()
422
423         // Create entries for the database fields.  The ID is created automatically.  The pinned SSL certificate information is not created unless added by the user.
424         domainContentValues.put(DOMAIN_NAME, domainName)
425         domainContentValues.put(ENABLE_JAVASCRIPT, javaScriptInt)
426         domainContentValues.put(COOKIES, cookiesInt)
427         domainContentValues.put(ENABLE_DOM_STORAGE, domStorageInt)
428         domainContentValues.put(ENABLE_FORM_DATA, formDataInt) // Form data can be removed once the minimum API >= 26.
429         domainContentValues.put(USER_AGENT, userAgentName)
430         domainContentValues.put(ENABLE_EASYLIST, easyListInt)
431         domainContentValues.put(ENABLE_EASYPRIVACY, easyPrivacyInt)
432         domainContentValues.put(ENABLE_FANBOYS_ANNOYANCE_LIST, fanboysAnnoyanceListInt)
433         domainContentValues.put(ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST, fanboysSocialBlockingListInt)
434         domainContentValues.put(ULTRALIST, ultraListInt)
435         domainContentValues.put(ENABLE_ULTRAPRIVACY, ultraPrivacyInt)
436         domainContentValues.put(BLOCK_ALL_THIRD_PARTY_REQUESTS, blockAllThirdPartyRequestsInt)
437         domainContentValues.put(FONT_SIZE, fontSizeInt)
438         domainContentValues.put(SWIPE_TO_REFRESH, swipeToRefreshInt)
439         domainContentValues.put(WEBVIEW_THEME, webViewThemeInt)
440         domainContentValues.put(WIDE_VIEWPORT, wideViewportInt)
441         domainContentValues.put(DISPLAY_IMAGES, displayImagesInt)
442
443         // Get a writable database handle.
444         val domainsDatabase = this.writableDatabase
445
446         // Insert a new row and store the resulting database ID.
447         val newDomainDatabaseId = domainsDatabase.insert(DOMAINS_TABLE, null, domainContentValues).toInt()
448
449         // Close the database handle.
450         domainsDatabase.close()
451
452         // Return the new domain database ID.
453         return newDomainDatabaseId
454     }
455
456     fun updateDomain(databaseId: Int, domainName: String, javaScript: Int, cookies: Int, domStorage: Int, formData: Int, userAgent: String, easyList: Int, easyPrivacy: Int, fanboysAnnoyance: Int,
457                      fanboysSocialBlocking: Int, ultraList: Int, ultraPrivacy: Int, blockAllThirdPartyRequests: Int, fontSize: Int, swipeToRefresh: Int, webViewTheme: Int, wideViewport: Int, displayImages: Int,
458                      pinnedSslCertificate: Boolean, pinnedIpAddresses: Boolean) {
459
460         // Instantiate a content values.
461         val domainContentValues = ContentValues()
462
463         // Add entries for each field in the database.
464         domainContentValues.put(DOMAIN_NAME, domainName)
465         domainContentValues.put(ENABLE_JAVASCRIPT, javaScript)
466         domainContentValues.put(COOKIES, cookies)
467         domainContentValues.put(ENABLE_DOM_STORAGE, domStorage)
468         domainContentValues.put(ENABLE_FORM_DATA, formData) // Form data can be removed once the minimum API >= 26.
469         domainContentValues.put(USER_AGENT, userAgent)
470         domainContentValues.put(ENABLE_EASYLIST, easyList)
471         domainContentValues.put(ENABLE_EASYPRIVACY, easyPrivacy)
472         domainContentValues.put(ENABLE_FANBOYS_ANNOYANCE_LIST, fanboysAnnoyance)
473         domainContentValues.put(ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST, fanboysSocialBlocking)
474         domainContentValues.put(ULTRALIST, ultraList)
475         domainContentValues.put(ENABLE_ULTRAPRIVACY, ultraPrivacy)
476         domainContentValues.put(BLOCK_ALL_THIRD_PARTY_REQUESTS, blockAllThirdPartyRequests)
477         domainContentValues.put(FONT_SIZE, fontSize)
478         domainContentValues.put(SWIPE_TO_REFRESH, swipeToRefresh)
479         domainContentValues.put(WEBVIEW_THEME, webViewTheme)
480         domainContentValues.put(WIDE_VIEWPORT, wideViewport)
481         domainContentValues.put(DISPLAY_IMAGES, displayImages)
482         domainContentValues.put(PINNED_SSL_CERTIFICATE, pinnedSslCertificate)
483         domainContentValues.put(PINNED_IP_ADDRESSES, pinnedIpAddresses)
484
485         // Get a writable database handle.
486         val domainsDatabase = this.writableDatabase
487
488         // Update the row for the specified database ID.
489         domainsDatabase.update(DOMAINS_TABLE, domainContentValues, "$ID = $databaseId", null)
490
491         // Close the database handle.
492         domainsDatabase.close()
493     }
494
495     fun updatePinnedSslCertificate(databaseId: Int, sslIssuedToCommonName: String, sslIssuedToOrganization: String, sslIssuedToOrganizationalUnit: String, sslIssuedByCommonName: String,
496                                    sslIssuedByOrganization: String, sslIssuedByOrganizationalUnit: String, sslStartDate: Long, sslEndDate: Long) {
497         // Instantiate a content values.
498         val pinnedSslCertificateContentValues = ContentValues()
499
500         // Add entries for each field in the certificate.
501         pinnedSslCertificateContentValues.put(SSL_ISSUED_TO_COMMON_NAME, sslIssuedToCommonName)
502         pinnedSslCertificateContentValues.put(SSL_ISSUED_TO_ORGANIZATION, sslIssuedToOrganization)
503         pinnedSslCertificateContentValues.put(SSL_ISSUED_TO_ORGANIZATIONAL_UNIT, sslIssuedToOrganizationalUnit)
504         pinnedSslCertificateContentValues.put(SSL_ISSUED_BY_COMMON_NAME, sslIssuedByCommonName)
505         pinnedSslCertificateContentValues.put(SSL_ISSUED_BY_ORGANIZATION, sslIssuedByOrganization)
506         pinnedSslCertificateContentValues.put(SSL_ISSUED_BY_ORGANIZATIONAL_UNIT, sslIssuedByOrganizationalUnit)
507         pinnedSslCertificateContentValues.put(SSL_START_DATE, sslStartDate)
508         pinnedSslCertificateContentValues.put(SSL_END_DATE, sslEndDate)
509
510         // Get a writable database handle.
511         val domainsDatabase = this.writableDatabase
512
513         // Update the row for the specified database ID.
514         domainsDatabase.update(DOMAINS_TABLE, pinnedSslCertificateContentValues, "$ID = $databaseId", null)
515
516         // Close the database handle.
517         domainsDatabase.close()
518     }
519
520     fun updatePinnedIpAddresses(databaseId: Int, ipAddresses: String) {
521         // Instantiate a content values.
522         val pinnedIpAddressesContentValues = ContentValues()
523
524         // Add the IP addresses to the content values.
525         pinnedIpAddressesContentValues.put(IP_ADDRESSES, ipAddresses)
526
527         // Get a writable database handle.
528         val domainsDatabase = this.writableDatabase
529
530         // Update the row for the database ID.
531         domainsDatabase.update(DOMAINS_TABLE, pinnedIpAddressesContentValues, "$ID = $databaseId", null)
532
533         // Close the database handle.
534         domainsDatabase.close()
535     }
536
537     fun deleteDomain(databaseId: Int) {
538         // Get a writable database handle.
539         val domainsDatabase = this.writableDatabase
540
541         // Delete the row for the specified database ID.
542         domainsDatabase.delete(DOMAINS_TABLE, "$ID = $databaseId", null)
543
544         // Close the database handle.
545         domainsDatabase.close()
546     }
547 }