Bump the target API to 32 (Android 12L). https://redmine.stoutner.com/issues/828
authorSoren Stoutner <soren@stoutner.com>
Mon, 28 Mar 2022 23:44:10 +0000 (16:44 -0700)
committerSoren Stoutner <soren@stoutner.com>
Mon, 28 Mar 2022 23:44:10 +0000 (16:44 -0700)
35 files changed:
app/build.gradle
app/src/main/AndroidManifest.xml
app/src/main/assets/de/about_changelog.html
app/src/main/assets/en/about_changelog.html
app/src/main/assets/es/about_changelog.html
app/src/main/assets/fr/about_changelog.html
app/src/main/assets/it/about_changelog.html
app/src/main/assets/pt-rBR/about_changelog.html
app/src/main/assets/pt-rBR/about_permissions.html
app/src/main/assets/pt-rBR/about_privacy_policy.html
app/src/main/assets/ru/about_changelog.html
app/src/main/assets/tr/about_changelog.html
app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksActivity.java
app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksDatabaseViewActivity.java
app/src/main/java/com/stoutner/privacybrowser/activities/DomainsActivity.java
app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java
app/src/main/java/com/stoutner/privacybrowser/adapters/PinnedMismatchPagerAdapter.kt
app/src/main/java/com/stoutner/privacybrowser/dialogs/AddDomainDialog.kt
app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateBookmarkFolderDialog.kt
app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDatabaseViewDialog.kt
app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDialog.kt
app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDatabaseViewDialog.kt
app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDialog.kt
app/src/main/java/com/stoutner/privacybrowser/dialogs/MoveToFolderDialog.kt
app/src/main/java/com/stoutner/privacybrowser/dialogs/PinnedMismatchDialog.kt
app/src/main/java/com/stoutner/privacybrowser/fragments/DomainSettingsFragment.java
app/src/main/java/com/stoutner/privacybrowser/helpers/BookmarksDatabaseHelper.java [deleted file]
app/src/main/java/com/stoutner/privacybrowser/helpers/BookmarksDatabaseHelper.kt [new file with mode: 0644]
app/src/main/java/com/stoutner/privacybrowser/helpers/CheckPinnedMismatchHelper.java [deleted file]
app/src/main/java/com/stoutner/privacybrowser/helpers/CheckPinnedMismatchHelper.kt [new file with mode: 0644]
app/src/main/java/com/stoutner/privacybrowser/helpers/DomainsDatabaseHelper.java [deleted file]
app/src/main/java/com/stoutner/privacybrowser/helpers/DomainsDatabaseHelper.kt [new file with mode: 0644]
app/src/main/java/com/stoutner/privacybrowser/helpers/ImportExportDatabaseHelper.java
app/src/main/java/com/stoutner/privacybrowser/views/NestedScrollWebView.kt
app/src/main/res/values-night/colors.xml

index 441cc71a0d4f7913b332b7cdb00bbeb9c5c7abfd..8469c24dc5e69944e23c25c325da244280039713 100644 (file)
@@ -27,7 +27,7 @@ android {
 
     defaultConfig {
         minSdk 23
-        targetSdk 31
+        targetSdk 32
         versionCode 59
         versionName "3.10.1"
 
index a773e56f40386a715a8c0b43cbd249d9b4544e8f..a9747840ad8b4a7a82bf33dba365f16cb2b59ef0 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 
 <!--
-  Copyright © 2015-2021 Soren Stoutner <soren@stoutner.com>.
+  Copyright © 2015-2022 Soren Stoutner <soren@stoutner.com>.
 
   This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
 
@@ -47,7 +47,8 @@
         <package android:name="org.sufficientlysecure.keychain" />
     </queries>
 
-    <!-- For API >= 23, app data is automatically backed up to Google cloud servers unless `android:allowBackup="false"` and `android:fullBackupContent="false"` is set. -->
+    <!-- For API >= 23, app data is automatically backed up to Google cloud servers unless `android:allowBackup="false"` and `android:fullBackupContent="false"` is set.
+        `tools:ignore="DataExtractionRules` removes the warning that backups can still transfer data device to device. -->
     <application
         android:label="@string/privacy_browser"
         android:icon="@mipmap/privacy_browser"
@@ -56,7 +57,7 @@
         android:fullBackupContent="false"
         android:supportsRtl="true"
         android:networkSecurityConfig="@xml/network_security_config"
-        tools:ignore="UnusedAttribute" >
+        tools:ignore="DataExtractionRules,UnusedAttribute" >
 
         <!-- If `android:name="android.webkit.WebView.MetricsOptOut"` is not `true` then `WebViews` will upload metrics to Google.  <https://developer.android.com/reference/android/webkit/WebView.html> -->
         <meta-data
             android:name="com.google.android.gms.ads.APPLICATION_ID"
             android:value="@string/google_app_id" />
 
-        <!-- Don't initialize the ad system in the free flavor until it is explicitly called. -->
-        <meta-data
-            android:name="com.google.android.gms.ads.DELAY_APP_MEASUREMENT_INIT"
-            android:value="true"/>
-
         <!-- The file provider is required to encrypt files with OpenKeychain. -->
         <provider
             android:name="androidx.core.content.FileProvider"
index dfa1f008b0cdcca9deede58e658de48444a85b7f..bcb18c2f2342e9a3591c2b1d5cce4ce63ce41633 100644 (file)
     </head>
 
     <body>
-        <h3>3.10.2 (version code 60)</h3>
-        <p>4 März 2022 - Mindest-API 23, Ziel-API 31</p>
-        <ul>
-            <li>Work around a <a href="https://redmine.stoutner.com/issues/811">scrolling bug</a> in Android System Webview >= 99.0.4844.48.</li>
-        </ul>
-
         <h3><a href="https://www.stoutner.com/privacy-browser-3-10-1/">3.10.1</a> (version code 59)</h3>
         <p><a href="https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=commitdiff;h=b1f88361f33fde911642f568e71936c33bdcc530">2 März 2022</a> - Mindest-API 23, Ziel-API 31</p>
         <ul>
index cd7d5ce01e7de21e29f1f3cf6a5962541b27ce18..47e6eea00869881aaff4588a088cada8369f6ff5 100644 (file)
     </head>
 
     <body>
-        <h3>3.10.2 (version code 60)</h3>
-        <p>4 March 2022 - minimum API 23, target API 31</p>
-        <ul>
-            <li>Work around a <a href="https://redmine.stoutner.com/issues/811">scrolling bug</a> in Android System Webview >= 99.0.4844.48.</li>
-        </ul>
-
         <h3><a href="https://www.stoutner.com/privacy-browser-3-10-1/">3.10.1</a> (version code 59)</h3>
         <p><a href="https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=commitdiff;h=b1f88361f33fde911642f568e71936c33bdcc530">2 March 2022</a> - minimum API 23, target API 31</p>
         <ul>
index 5a9cc64c886a188a2a1decf4e898f3fd20e9e39e..5d1c3516b3c3ea44f8b70bbf97a46dd47dd1ad88 100644 (file)
     </head>
 
     <body>
-        <h3>3.10.2 (código de versión 60)</h3>
-        <p>4 de marzo de 2022 - API mínimo 23, API objetivo 31</p>
-        <ul>
-            <li>Work around a <a href="https://redmine.stoutner.com/issues/811">scrolling bug</a> in Android System Webview >= 99.0.4844.48.</li>
-        </ul>
-
         <h3><a href="https://www.stoutner.com/privacy-browser-3-10-1/">3.10.1</a> (código de versión 59)</h3>
         <p><a href="https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=commitdiff;h=b1f88361f33fde911642f568e71936c33bdcc530">2 de marzo de 2022</a> - API mínimo 23, API objetivo 31</p>
         <ul>
index d767169aea7b26e3ae659c2538832b5b3a3b1e12..184604b3bd88b092ef9fd93d5cb66bb70e73ef19 100644 (file)
     </head>
 
     <body>
-        <h3>3.10.2 (version du code 60)</h3>
-        <p>4 Mars 2022 - API minimale : 23, API optimale : 31</p>
-        <ul>
-            <li>Work around a <a href="https://redmine.stoutner.com/issues/811">scrolling bug</a> in Android System Webview >= 99.0.4844.48.</li>
-        </ul>
-
         <h3><a href="https://www.stoutner.com/privacy-browser-3-10-1/">3.10.1</a> (version du code 59)</h3>
         <p><a href="https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=commitdiff;h=b1f88361f33fde911642f568e71936c33bdcc530">2 Mars 2022</a> - API minimale : 23, API optimale : 31</p>
         <ul>
index da8831b099bd82f443f63816135a17bfc9b5c733..23451a1a986d63ce6e1ab26ba8c21fbe8e9afda0 100644 (file)
     </head>
 
     <body>
-        <h3>3.10.2 (versione codice 60)</h3>
-        <p>4 Marzo 2022 - minima API 23, target API 31</p>
-        <ul>
-            <li>Work around a <a href="https://redmine.stoutner.com/issues/811">scrolling bug</a> in Android System Webview >= 99.0.4844.48.</li>
-        </ul>
-
         <h3><a href="https://www.stoutner.com/privacy-browser-3-10-1/">3.10.1</a> (versione codice 59)</h3>
         <p><a href="https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=commitdiff;h=b1f88361f33fde911642f568e71936c33bdcc530">2 Marzo 2022</a> - minima API 23, target API 31</p>
         <ul>
index b869a264204d5af7bd8522f629bca29b3c76fd7a..6c630143448714d1d0736535d9489e44d6c256fc 100644 (file)
     </head>
 
     <body>
-        <h3>3.10.2 (código da versão 60)</h3>
-        <p>4 March 2022 - API mínimo 23, API alvo 31</p>
-        <ul>
-            <li>Work around a <a href="https://redmine.stoutner.com/issues/811">scrolling bug</a> in Android System Webview >= 99.0.4844.48.</li>
-        </ul>
-
         <h3><a href="https://www.stoutner.com/privacy-browser-3-10-1/">3.10.1</a> (código da versão 59)</h3>
         <p><a href="https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=commitdiff;h=b1f88361f33fde911642f568e71936c33bdcc530">2 March 2022</a> - API mínimo 23, API alvo 31</p>
         <ul>
index 948c940d5a183f072d3fca26f0276768c7b59d69..1e024cedd5ccd5fc5e3c15c207f5a6e67b9897ac 100644 (file)
     </head>
 
     <body>
-        <h3>Have full network access</h3>
+        <h3>Tenha acesso total à rede</h3>
         <p><a href="https://developer.android.com/reference/android/Manifest.permission.html#INTERNET">android.permission.INTERNET</a></p>
-        <p>Required for the WebView to access the internet. Without this permission, Privacy Browser would be “No Browser: Protecting Your Privacy by Staying Completely Off the Internet”.</p>
+        <p>Necessário para o WebView acessar a internet. Sem essa permissão, o Privacy Browser seria “Anti-navegador: protegendo sua privacidade ficando completamente fora da Internet”.</p>
 
-        <h3>Install shortcuts</h3>
+        <h3>Instalar atalhos</h3>
         <p><a href="https://developer.android.com/reference/android/Manifest.permission.html#INSTALL_SHORTCUT">com.android.launcher.permission.INSTALL_SHORTCUT</a></p>
-        <p>Required to add shortcuts for websites to the launcher desktop.</p>
+        <p>Necessário para adicionar atalhos para sites na área de trabalho inicial.</p>
     </body>
 </html>
\ No newline at end of file
index 9eab3cd4c3415d8bcdcb7afe631b37ebf8382d03..53b46b8f142aa2ee6d513409d9d69c154f50137e 100644 (file)
 
     <body>
         <h3>Privacy Browser</h3>
-        <p><strong class="red">Privacy Browser does not collect any user information.</strong></p>
+        <p><strong class="red">Privacy Browser não coleta nenhuma informação do usuário.</strong></p>
 
 
         <h3>Google Play</h3>
-        <p>Google Play has its <a href="https://policies.google.com/privacy">own privacy policy</a>.
-            Google provides <em>anonymized summary installation information</em> to developers, including the number of installs organized by the following categories.</p>
+        <p>Google Play tem sua <a href="https://policies.google.com/privacy">própria política de privacidade</a>.
+            Google fornece <em>informações de instalação resumidas anônimas</em> aos desenvolvedores, incluindo o número de instalações organizadas pelas categorias a seguir.</p>
         <ul>
-            <li><item>Android version</item> (eg. Android 7.1)</li>
-            <li><item>Device</item> (eg. Samsung Galaxy S6 [zeroflte])</li>
-            <li><item>Tablets</item> (eg. Tablets 10" and above)</li>
-            <li><item>Country</item> (eg. United States)</li>
-            <li><item>Language</item> (eg. English [United States])</li>
-            <li><item>App version</item> (eg. 14)</li>
-            <li><item>Carrier</item> (eg. T-Mobile - US)</li>
+            <li><item>Versão do Android</item> (eg. Android 7.1)</li>
+            <li><item>Dispositivo</item> (eg. Samsung Galaxy S6 [zeroflte])</li>
+            <li><item>Tablets</item> (eg. Tablets 10" e superior)</li>
+            <li><item>País</item> (eg. Estados Unidos)</li>
+            <li><item>Idioma</item> (eg. Inglês [Estados Unidos])</li>
+            <li><item>Versão do aplicativo</item> (eg. 14)</li>
+            <li><item>Operadora</item> (eg. T-Mobile - US)</li>
         </ul>
 
 
-        <h3>Google Play Ratings</h3>
-        <p>Google Play has its <a href="https://policies.google.com/privacy">own privacy policy</a>.
-            Google provides developers with <em>anonymized summaries</em> of the following information related to user ratings.</p>
+        <h3>Classificações do Google Play</h3>
+        <p>Google Play tem sua <a href="https://policies.google.com/privacy">própria política de privacidade</a>.
+            Google fornece aos desenvolvedores <em>resumos anônimos</em> das seguintes informações relacionadas às classificações dos usuários.</p>
         <ul>
-            <li><item>Country</item> (eg. United States)</li>
-            <li><item>Language</item> (eg. English)</li>
-            <li><item>App version</item> (eg. 14)</li>
-            <li><item>Android version</item> (eg. Android 7.1)</li>
-            <li><item>Device</item> (eg. Google Nexus 5X [bullhead])</li>
-            <li><item>Tablets</item> (eg. Tablets 10" and above)</li>
+            <li><item>País</item> (eg. Estados Unidos)</li>
+            <li><item>Idioma</item> (eg. Inglês)</li>
+            <li><item>Versão do Aplicativo</item> (eg. 14)</li>
+            <li><item>Versão do Android</item> (eg. Android 7.1)</li>
+            <li><item>Dispositivo</item> (eg. Google Nexus 5X [bullhead])</li>
+            <li><item>Tablets</item> (eg. Tablets 10" e superior)</li>
         </ul>
 
 
-        <h3>Google Play Reviews</h3>
-        <p>Google Play has its <a href="https://policies.google.com/privacy">own privacy policy</a>.
-            In addition to the name of the reviewer, the rating, and the text of the review (which are all available publicly), Google provides some or all of the following information to the developer.</p>
+        <h3>Avaliações do Google Play</h3>
+        <p>Google Play tem sua <a href="https://policies.google.com/privacy">própria política de privacidade</a>.
+            Além do nome do avaliador, da classificação e do texto da avaliação (todos disponíveis publicamente), o Google fornece algumas ou todas as informações a seguir ao desenvolvedor.</p>
         <ul>
-            <li><item>Version code</item> (eg. 7)</li>
-            <li><item>Version name</item> (eg. 1.6)</li>
-            <li><item>Android version</item> (eg. Android 5.1)</li>
-            <li><item>Device</item> (eg. Galaxy S6 Edge+ [zenlte])</li>
-            <li><item>Manufacturer</item> (eg. Samsung)</li>
-            <li><item>Device type</item> (eg. Phone)</li>
-            <li><item>CPU make</item> (eg. Samsung)</li>
-            <li><item>CPU model</item> (eg. Exynos 7420)</li>
-            <li><item>Screen density</item> (eg. 560 dpi)</li>
-            <li><item>Screen size</item> (eg. 2560 x 1440)</li>
+            <li><item>Código da versão</item> (eg. 7)</li>
+            <li><item>Nome da versão</item> (eg. 1.6)</li>
+            <li><item>Versão do Android</item> (eg. Android 5.1)</li>
+            <li><item>Dispositivo</item> (eg. Galaxy S6 Edge+ [zenlte])</li>
+            <li><item>Fabricante</item> (eg. Samsung)</li>
+            <li><item>Tipo de dispositivo</item> (eg. Phone)</li>
+            <li><item>Fabricante da CPU</item> (eg. Samsung)</li>
+            <li><item>Modelo da CPU</item> (eg. Exynos 7420)</li>
+            <li><item>Densidade da tela</item> (eg. 560 dpi)</li>
+            <li><item>Tamanho da tela</item> (eg. 2560 x 1440)</li>
             <li><item>RAM</item> (eg. 4096 MB)</li>
-            <li><item>Native platform</item> (eg. armeabi-v7a,armeabi,arm64v8a)</li>
-            <li><item>OpenGL ES version</item> (eg. 3.1)</li>
-            <li><item>Device language</item> (eg. English)</li>
+            <li><item>Plataforma nativa</item> (eg. armeabi-v7a,armeabi,arm64v8a)</li>
+            <li><item>Versão OpenGL ES</item> (eg. 3.1)</li>
+            <li><item>Idioma do dispositivo</item> (eg. Inglês)</li>
         </ul>
 
 
-        <h3>Direct Communications</h3>
-        <p>Users may choose to send direct communications to Stoutner, like email messages and comments on <a href="https://www.stoutner.com/">stoutner.com</a>.</p>
+        <h3>Comunicações Diretas</h3>
+        <p>Os usuários podem optar por enviar comunicações diretas à Stoutner, como mensagens de e-mail e comentários em <a href="https://www.stoutner.com/">stoutner.com</a>.</p>
 
 
-        <h3>Use of Information</h3>
-        <p><strong class="blue">Stoutner may use this information to assist in the development of Privacy Browser and communicate the status of the project to users.</strong>
-            <strong class="red">Stoutner will never sell this information nor transfer it to any third party that would use it for advertising or marketing.</strong></p>
+        <h3>Uso de informações</h3>
+        <p><strong class="blue">Stoutner pode usar essas informações para auxiliar no desenvolvimento do Privacy Browser e comunicar o status do projeto aos usuários.</strong>
+            <strong class="red">A Stoutner nunca venderá essas informações nem as transferirá para terceiros que as usariam para publicidade ou marketing.</strong></p>
 
         <hr />
-        <p style="text-align: center;"><em>Revision 1.7, 14 May 2019</em></p>
+        <p style="text-align: center;"><em>Revisão 1.7, 14 de Maio de 2019</em></p>
     </body>
 </html>
\ No newline at end of file
index 51f8ecc5062f3135995770a21e65380ccd0cf229..e7575a856bcb50ac7d4855a167b3a4273338b717 100644 (file)
     </head>
 
     <body>
-        <h3>3.10.2 (код версии 60)</h3>
-        <p>4 мая 2022 года - минимальный API 23, целевой API 31</p>
-        <ul>
-            <li>Work around a <a href="https://redmine.stoutner.com/issues/811">scrolling bug</a> in Android System Webview >= 99.0.4844.48.</li>
-        </ul>
-
         <h3><a href="https://www.stoutner.com/privacy-browser-3-10-1/">3.10.1</a> (код версии 59)</h3>
         <p><a href="https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=commitdiff;h=b1f88361f33fde911642f568e71936c33bdcc530">2 мая 2022 года</a> - минимальный API 23, целевой API 31</p>
         <ul>
index 53f21dc41c7b1f9dc6a88f3ded021ee2a73afaba..dbc2d5df19fda1b7164e37a1523f9d7675e7bd6c 100644 (file)
     </head>
 
     <body>
-        <h3>3.10.2 (version code 60)</h3>
-        <p>4 Mart 2022 - minimum API 23, target API 31</p>
-        <ul>
-            <li>Work around a <a href="https://redmine.stoutner.com/issues/811">scrolling bug</a> in Android System Webview >= 99.0.4844.48.</li>
-        </ul>
-
         <h3><a href="https://www.stoutner.com/privacy-browser-3-10-1/">3.10.1</a> (version code 59)</h3>
         <p><a href="https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=commitdiff;h=b1f88361f33fde911642f568e71936c33bdcc530">2 Mart 2022</a> - minimum API 23, target API 31</p>
         <ul>
index cea440a4239b0eb78f9bbf60c2ea856240e49a5c..ef2bb4ed9ea19513bb17f78ea7efa0d46dfabcc1 100644 (file)
@@ -189,9 +189,8 @@ public class BookmarksActivity extends AppCompatActivity implements CreateBookma
         // Display the home arrow on the app bar.
         appBar.setDisplayHomeAsUpEnabled(true);
 
-        // Initialize the database helper.  `this` specifies the context.  The two `nulls` do not specify the database name or a `CursorFactory`.
-        // The `0` specifies a database version, but that is ignored and set instead using a constant in `BookmarksDatabaseHelper`.
-        bookmarksDatabaseHelper = new BookmarksDatabaseHelper(this, null, null, 0);
+        // Initialize the database helper.
+        bookmarksDatabaseHelper = new BookmarksDatabaseHelper(this);
 
         // Load the home folder.
         loadFolder();
@@ -451,7 +450,7 @@ public class BookmarksActivity extends AppCompatActivity implements CreateBookma
                     boolean isFolder = (bookmarksCursor.getInt(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.IS_FOLDER)) == 1);
 
                     // Get the selected bookmark database ID.
-                    int databaseId = bookmarksCursor.getInt(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper._ID));
+                    int databaseId = bookmarksCursor.getInt(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.ID));
 
                     // Show the edit bookmark or edit bookmark folder dialog.
                     if (isFolder) {
@@ -1055,7 +1054,7 @@ public class BookmarksActivity extends AppCompatActivity implements CreateBookma
             folderCursor.moveToPosition(i);
 
             // Get the database ID of the item.
-            int itemDatabaseId = folderCursor.getInt(folderCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper._ID));
+            int itemDatabaseId = folderCursor.getInt(folderCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.ID));
 
             // If this is a folder, recursively count the contents first.
             if (bookmarksDatabaseHelper.isFolder(itemDatabaseId)) {
@@ -1084,7 +1083,7 @@ public class BookmarksActivity extends AppCompatActivity implements CreateBookma
             folderCursor.moveToPosition(i);
 
             // Get the database ID of the item.
-            int itemDatabaseId = folderCursor.getInt(folderCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper._ID));
+            int itemDatabaseId = folderCursor.getInt(folderCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.ID));
 
             // If this is a folder, recursively delete the contents first.
             if (bookmarksDatabaseHelper.isFolder(itemDatabaseId)) {
index 91afa6d64120a2d15d1bbba2b53083b4e7ae22bc..dbfda387903de7d0fc0a9c40b42467fb8a16249b 100644 (file)
@@ -153,11 +153,11 @@ public class BookmarksDatabaseViewActivity extends AppCompatActivity implements
         actionBar.setCustomView(R.layout.spinner);
         actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_HOME_AS_UP);
 
-        // Initialize the database handler.  The `0` is to specify a database version, but that is set instead using a constant in `BookmarksDatabaseHelper`.
-        bookmarksDatabaseHelper = new BookmarksDatabaseHelper(this, null, null, 0);
+        // Initialize the database handler.
+        bookmarksDatabaseHelper = new BookmarksDatabaseHelper(this);
 
         // Setup a matrix cursor for "All Folders" and "Home Folder".
-        String[] matrixCursorColumnNames = {BookmarksDatabaseHelper._ID, BookmarksDatabaseHelper.BOOKMARK_NAME};
+        String[] matrixCursorColumnNames = {BookmarksDatabaseHelper.ID, BookmarksDatabaseHelper.BOOKMARK_NAME};
         MatrixCursor matrixCursor = new MatrixCursor(matrixCursorColumnNames);
         matrixCursor.addRow(new Object[]{ALL_FOLDERS_DATABASE_ID, getString(R.string.all_folders)});
         matrixCursor.addRow(new Object[]{HOME_FOLDER_DATABASE_ID, getString(R.string.home_folder)});
@@ -296,7 +296,7 @@ public class BookmarksDatabaseViewActivity extends AppCompatActivity implements
                 boolean isFolder = (cursor.getInt(cursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.IS_FOLDER)) == 1);
 
                 // Get the database ID from the `Cursor` and display it in `bookmarkDatabaseIdTextView`.
-                int bookmarkDatabaseId = cursor.getInt(cursor.getColumnIndexOrThrow(BookmarksDatabaseHelper._ID));
+                int bookmarkDatabaseId = cursor.getInt(cursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.ID));
                 TextView bookmarkDatabaseIdTextView = view.findViewById(R.id.bookmarks_databaseview_database_id);
                 bookmarkDatabaseIdTextView.setText(String.valueOf(bookmarkDatabaseId));
 
@@ -472,7 +472,7 @@ public class BookmarksDatabaseViewActivity extends AppCompatActivity implements
                             // Get the position of the folder in the bookmarks cursor.
                             while ((folderPosition < 0) && (bookmarksCursor.getPosition() < bookmarksCursor.getCount())) {
                                 // Check if the folder database ID matches the bookmark database ID.
-                                if (folderDatabaseId == bookmarksCursor.getInt(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper._ID))) {
+                                if (folderDatabaseId == bookmarksCursor.getInt(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.ID))) {
                                     // Get the folder position.
                                     folderPosition = bookmarksCursor.getPosition();
 
@@ -754,7 +754,7 @@ public class BookmarksDatabaseViewActivity extends AppCompatActivity implements
 
         while (folderCursor.getPosition() < folderCursor.getCount()) {
             // Get the bookmark database ID.
-            int bookmarkId = folderCursor.getInt(folderCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper._ID));
+            int bookmarkId = folderCursor.getInt(folderCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.ID));
 
             // Move the bookmarks cursor to the first position.
             bookmarksCursor.moveToFirst();
@@ -765,7 +765,7 @@ public class BookmarksDatabaseViewActivity extends AppCompatActivity implements
             // Get the position of this bookmark in the bookmarks cursor.
             while ((bookmarkPosition < 0) && (bookmarksCursor.getPosition() < bookmarksCursor.getCount())) {
                 // Check if the bookmark IDs match.
-                if (bookmarkId == bookmarksCursor.getInt(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper._ID))) {
+                if (bookmarkId == bookmarksCursor.getInt(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.ID))) {
                     // Get the bookmark position.
                     bookmarkPosition = bookmarksCursor.getPosition();
 
index 2ef534f1d4286fb54557e8926efcf3b0bf648c82..b9a4fb7d3271f3e8299bb1957624e4558ffca3c5 100644 (file)
@@ -202,8 +202,8 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo
         // Set the back arrow on the action bar.
         actionBar.setDisplayHomeAsUpEnabled(true);
 
-        // Initialize the database handler.  The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`.
-        domainsDatabaseHelper = new DomainsDatabaseHelper(this, null, null, 0);
+        // Initialize the database handler.
+        domainsDatabaseHelper = new DomainsDatabaseHelper(this);
 
         // Determine if we are in two pane mode.  `domain_settings_fragment_container` does not exist on devices with a width less than 900dp.
         twoPanedMode = (findViewById(R.id.domain_settings_fragment_container) != null);
@@ -876,7 +876,7 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo
                 domainsCursor.moveToPosition(i);
 
                 // Get the database ID for this position.
-                int currentDatabaseId = domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper._ID));
+                int currentDatabaseId = domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ID));
 
                 // Set `highlightedDomainPosition` if the database ID for this matches `highlightedDomainDatabaseId`.
                 if (highlightedDomainDatabaseId == currentDatabaseId) {
@@ -889,7 +889,7 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo
 
             // Get the database ID for the highlighted domain.
             domainsCursor.moveToPosition(highlightedDomainPosition);
-            currentDomainDatabaseId = domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper._ID));
+            currentDomainDatabaseId = domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ID));
 
             // Create an arguments bundle.
             Bundle argumentsBundle = new Bundle();
index 3d27a4cf61d0782be71bbe673cc29dad58082b41..ecab353f0dd1b0b6cea8e4da652ee4cddf7555a2 100644 (file)
@@ -118,6 +118,7 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
 import androidx.viewpager.widget.ViewPager;
 import androidx.webkit.WebSettingsCompat;
 import androidx.webkit.WebViewFeature;
+import kotlin.Pair;
 
 import com.google.android.material.appbar.AppBarLayout;
 import com.google.android.material.floatingactionbutton.FloatingActionButton;
@@ -529,7 +530,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         }
 
         // Enable the drawing of the entire webpage.  This makes it possible to save a website image.  This must be done before anything else happens with the WebView.
-        //WebView.enableSlowWholeDocumentDraw();    Temporarily disabled due to <https://redmine.stoutner.com/issues/811>.
+        WebView.enableSlowWholeDocumentDraw();
 
         // Set the theme.
         setTheme(R.style.PrivacyBrowser);
@@ -1933,8 +1934,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 Uri currentUri = Uri.parse(currentWebView.getUrl());
                 String currentDomain = currentUri.getHost();
 
-                // Initialize the database handler.  The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`.
-                DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(this, null, null, 0);
+                // Initialize the database handler.
+                DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(this);
 
                 // Create the domain and store the database ID.
                 int newDomainDatabaseId = domainsDatabaseHelper.addDomain(currentDomain);
@@ -3363,8 +3364,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         drawerLayout.setDrawerTitle(GravityCompat.START, getString(R.string.navigation_drawer));
         drawerLayout.setDrawerTitle(GravityCompat.END, getString(R.string.bookmarks));
 
-        // Initialize the bookmarks database helper.  The `0` specifies a database version, but that is ignored and set instead using a constant in `BookmarksDatabaseHelper`.
-        bookmarksDatabaseHelper = new BookmarksDatabaseHelper(this, null, null, 0);
+        // Initialize the bookmarks database helper.
+        bookmarksDatabaseHelper = new BookmarksDatabaseHelper(this);
 
         // Initialize `currentBookmarksFolder`.  `""` is the home folder in the database.
         currentBookmarksFolder = "";
@@ -3700,8 +3701,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 }
             }
 
-            // Initialize the database handler.  The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`.
-            DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(this, null, null, 0);
+            // Initialize the database handler.
+            DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(this);
 
             // Get a full cursor from `domainsDatabaseHelper`.
             Cursor domainNameCursor = domainsDatabaseHelper.getDomainNameCursorOrderedByDomain();
@@ -3712,7 +3713,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             // Get the domain name column index.
             int domainNameColumnIndex = domainNameCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DOMAIN_NAME);
 
-            // Populate `domainSettingsSet`.
+            // Populate the domain settings set.
             for (int i = 0; i < domainNameCursor.getCount(); i++) {
                 // Move the domains cursor to the current row.
                 domainNameCursor.moveToPosition(i);
@@ -3721,7 +3722,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 domainSettingsSet.add(domainNameCursor.getString(domainNameColumnIndex));
             }
 
-            // Close `domainNameCursor.
+            // Close the domain name cursor.
             domainNameCursor.close();
 
             // Initialize the domain name in database variable.
@@ -3740,7 +3741,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             }
 
             // Check all the subdomains of the host name against wildcard domains in the domain cursor.
-            while (!nestedScrollWebView.getDomainSettingsApplied() && newHostName.contains(".")) {  // Stop checking if domain settings are already applied or there are no more `.` in the host name.
+            while (!nestedScrollWebView.getDomainSettingsApplied() && newHostName.contains(".")) {  // Stop checking if domain settings are already applied or there are no more `.` in the hostname.
                 if (domainSettingsSet.contains("*." + newHostName)) {  // Check the host name prepended by `*.`.
                     // Set the domain settings applied tracker to true.
                     nestedScrollWebView.setDomainSettingsApplied(true);
@@ -3776,12 +3777,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             String[] userAgentDataArray = getResources().getStringArray(R.array.user_agent_data);
 
             if (nestedScrollWebView.getDomainSettingsApplied()) {  // The url has custom domain settings.
-                // Get a cursor for the current host and move it to the first position.
+                // Remove the incorrect lint warning below that the domain name in database might be null.
+                assert domainNameInDatabase != null;
+
+                // Get a cursor for the current host.
                 Cursor currentDomainSettingsCursor = domainsDatabaseHelper.getCursorForDomainName(domainNameInDatabase);
+
+                // Move to the first position.
                 currentDomainSettingsCursor.moveToFirst();
 
                 // Get the settings from the cursor.
-                nestedScrollWebView.setDomainSettingsDatabaseId(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper._ID)));
+                nestedScrollWebView.setDomainSettingsDatabaseId(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ID)));
                 nestedScrollWebView.getSettings().setJavaScriptEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_JAVASCRIPT)) == 1);
                 nestedScrollWebView.setAcceptCookies(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.COOKIES)) == 1);
                 nestedScrollWebView.getSettings().setDomStorageEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ENABLE_DOM_STORAGE)) == 1);
@@ -6095,11 +6101,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Proceed to the website if the current SSL website certificate matches the pinned domain certificate.
                 if (nestedScrollWebView.hasPinnedSslCertificate()) {
                     // Get the pinned SSL certificate.
-                    ArrayList<Object> pinnedSslCertificateArrayList = nestedScrollWebView.getPinnedSslCertificate();
+                    Pair<String[], Date[]> pinnedSslCertificatePair = nestedScrollWebView.getPinnedSslCertificate();
 
                     // Extract the arrays from the array list.
-                    String[] pinnedSslCertificateStringArray = (String[]) pinnedSslCertificateArrayList.get(0);
-                    Date[] pinnedSslCertificateDateArray = (Date[]) pinnedSslCertificateArrayList.get(1);
+                    String[] pinnedSslCertificateStringArray = pinnedSslCertificatePair.getFirst();
+                    Date[] pinnedSslCertificateDateArray = pinnedSslCertificatePair.getSecond();
 
                     // Check if the current SSL certificate matches the pinned certificate.
                     if (currentWebsiteIssuedToCName.equals(pinnedSslCertificateStringArray[0]) && currentWebsiteIssuedToOName.equals(pinnedSslCertificateStringArray[1]) &&
index aad20f89c469122961e915a7b1fb0834c475d953..8953d8545b53d493e5be160c49139c761530964f 100644 (file)
@@ -129,12 +129,12 @@ class PinnedMismatchPagerAdapter(private val context: Context, private val layou
             currentSslEndDate = sslCertificate.validNotAfterDate
         }
 
-        // Get the pinned SSL certificate.
-        val pinnedSslCertificateArrayList = nestedScrollWebView.getPinnedSslCertificate()
+        // Get the pinned SSL certificate pair.
+        val pinnedSslCertificatePair = nestedScrollWebView.getPinnedSslCertificate()
 
         // Extract the arrays from the array list.
-        val pinnedSslCertificateStringArray = pinnedSslCertificateArrayList[0] as Array<*>
-        val pinnedSslCertificateDateArray = pinnedSslCertificateArrayList[1] as Array<*>
+        val pinnedSslCertificateStringArray = pinnedSslCertificatePair.first
+        val pinnedSslCertificateDateArray = pinnedSslCertificatePair.second
 
         // Setup the domain name spannable string builder.
         val domainNameStringBuilder = SpannableStringBuilder(domainNameLabel + domainName)
@@ -182,19 +182,8 @@ class PinnedMismatchPagerAdapter(private val context: Context, private val layou
             issuedByCNameStringBuilder = SpannableStringBuilder(cNameLabel + pinnedSslCertificateStringArray[3])
             issuedByONameStringBuilder = SpannableStringBuilder(oNameLabel + pinnedSslCertificateStringArray[4])
             issuedByUNameStringBuilder = SpannableStringBuilder(uNameLabel + pinnedSslCertificateStringArray[5])
-
-            // Set the dates if they aren't null.  Formatting a null date causes a crash.
-            startDateStringBuilder = if (pinnedSslCertificateDateArray[0] == null) {
-                SpannableStringBuilder(startDateLabel)
-            } else {
-                SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(pinnedSslCertificateDateArray[0]))
-            }
-
-            endDateStringBuilder = if (pinnedSslCertificateDateArray[1] == null) {
-                SpannableStringBuilder(endDateLabel)
-            } else {
-                SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(pinnedSslCertificateDateArray[1]))
-            }
+            startDateStringBuilder = SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(pinnedSslCertificateDateArray[0]))
+            endDateStringBuilder = SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(pinnedSslCertificateDateArray[1]))
         }
 
         // Create the color spans.
index c67e23b0870fb910ad3541888c251c20cee398e8..1eb8858ac1585324355f9678a7357af5c620f111 100644 (file)
@@ -125,8 +125,8 @@ class AddDomainDialog : DialogFragment() {
         // The alert dialog must be shown before the contents can be modified.
         alertDialog.show()
 
-        // Initialize the domains database helper.  The `0` specifies the database version, but that is ignored and set instead using a constant in domains database helper.
-        val domainsDatabaseHelper = DomainsDatabaseHelper(context, null, null, 0)
+        // Initialize the domains database helper.
+        val domainsDatabaseHelper = DomainsDatabaseHelper(requireContext())
 
         // Get handles for the views in the alert dialog.
         val addDomainEditText = alertDialog.findViewById<EditText>(R.id.domain_name_edittext)!!
index 553e5b839618c28eda944a680b371dffcc324748..3327e1f43e691dd96f18a4913a861919f8af72af 100644 (file)
@@ -179,8 +179,8 @@ class CreateBookmarkFolderDialog : DialogFragment() {
             defaultIconRadioButton.isChecked = false
         }
 
-        // Initialize the database helper.  The `0` specifies a database version, but that is ignored and set instead using a constant in `BookmarksDatabaseHelper`.
-        val bookmarksDatabaseHelper = BookmarksDatabaseHelper(context, null, null, 0)
+        // Initialize the database helper.
+        val bookmarksDatabaseHelper = BookmarksDatabaseHelper(requireContext())
 
         // Enable the create button if the new folder name is unique.
         folderNameEditText.addTextChangedListener(object: TextWatcher {
index 4e4600deb46921a6dd81d15c2420294133ff3faa..2e5a04f44f337270e154ff4f242ba7bb40471576 100644 (file)
@@ -126,8 +126,8 @@ class EditBookmarkDatabaseViewDialog : DialogFragment() {
         // Convert the favorite icon byte array to a bitmap.
         val favoriteIconBitmap = BitmapFactory.decodeByteArray(favoriteIconByteArray, 0, favoriteIconByteArray.size)
 
-        // Initialize the database helper.  The `0` specifies a database version, but that is ignored and set instead using a constant in `BookmarksDatabaseHelper`.
-        val bookmarksDatabaseHelper = BookmarksDatabaseHelper(context, null, null, 0)
+        // Initialize the database helper.
+        val bookmarksDatabaseHelper = BookmarksDatabaseHelper(requireContext())
 
         // Get a cursor with the selected bookmark.
         val bookmarkCursor = bookmarksDatabaseHelper.getBookmark(bookmarkDatabaseId)
@@ -190,7 +190,7 @@ class EditBookmarkDatabaseViewDialog : DialogFragment() {
         val currentDisplayOrder = bookmarkCursor.getInt(bookmarkCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.DISPLAY_ORDER))
 
         // Set the database ID.
-        databaseIdTextView.text = bookmarkCursor.getInt(bookmarkCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper._ID)).toString()
+        databaseIdTextView.text = bookmarkCursor.getInt(bookmarkCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.ID)).toString()
 
         // Get the current favorite icon byte array from the cursor.
         val currentIconByteArray = bookmarkCursor.getBlob(bookmarkCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.FAVORITE_ICON))
@@ -209,7 +209,7 @@ class EditBookmarkDatabaseViewDialog : DialogFragment() {
         urlEditText.setText(currentUrl)
 
         // Create an an array of column names for the matrix cursor comprised of the ID and the name.
-        val matrixCursorColumnNamesArray = arrayOf(BookmarksDatabaseHelper._ID, BookmarksDatabaseHelper.BOOKMARK_NAME)
+        val matrixCursorColumnNamesArray = arrayOf(BookmarksDatabaseHelper.ID, BookmarksDatabaseHelper.BOOKMARK_NAME)
 
         // Create a matrix cursor based on the column names array.
         val matrixCursor = MatrixCursor(matrixCursorColumnNamesArray)
index e30cf5db3163432a74dd8f1651524b1c1c2219a0..00c7a0797b7aa4827bc3e1f0444bc5366f3567ac 100644 (file)
@@ -114,8 +114,8 @@ class EditBookmarkDialog : DialogFragment() {
         // Convert the favorite icon byte array to a bitmap.
         val favoriteIconBitmap = BitmapFactory.decodeByteArray(favoriteIconByteArray, 0, favoriteIconByteArray.size)
 
-        // Initialize the bookmarks database helper.  The `0` specifies a database version, but that is ignored and set instead using a constant in `BookmarksDatabaseHelper`.
-        val bookmarksDatabaseHelper = BookmarksDatabaseHelper(context, null, null, 0)
+        // Initialize the bookmarks database helper.
+        val bookmarksDatabaseHelper = BookmarksDatabaseHelper(requireContext())
 
         // Get a cursor with the selected bookmark.
         val bookmarkCursor = bookmarksDatabaseHelper.getBookmark(selectedBookmarkDatabaseId)
index d7f29bf7bd301f73224cdf7162f8dd46b466c792..8ef895c4969328841bd94051305c62db33c4702f 100644 (file)
@@ -119,8 +119,8 @@ class EditBookmarkFolderDatabaseViewDialog : DialogFragment() {
         // Convert the favorite icon byte array to a bitmap.
         val favoriteIconBitmap = BitmapFactory.decodeByteArray(favoriteIconByteArray, 0, favoriteIconByteArray.size)
 
-        // Initialize the bookmarks database helper.   The `0` specifies a database version, but that is ignored and set instead using a constant in `BookmarksDatabaseHelper`.
-        val bookmarksDatabaseHelper = BookmarksDatabaseHelper(context, null, null, 0)
+        // Initialize the bookmarks database helper.
+        val bookmarksDatabaseHelper = BookmarksDatabaseHelper(requireContext())
 
         // Get a cursor with the selected bookmark.
         val folderCursor = bookmarksDatabaseHelper.getBookmark(folderDatabaseId)
@@ -184,7 +184,7 @@ class EditBookmarkFolderDatabaseViewDialog : DialogFragment() {
         val parentFolder = folderCursor.getString(folderCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.PARENT_FOLDER))
 
         // Populate the database ID text view.
-        databaseIdTextView.text = folderCursor.getInt(folderCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper._ID)).toString()
+        databaseIdTextView.text = folderCursor.getInt(folderCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.ID)).toString()
 
         // Get the current favorite icon byte array from the cursor.
         val currentIconByteArray = folderCursor.getBlob(folderCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.FAVORITE_ICON))
@@ -202,7 +202,7 @@ class EditBookmarkFolderDatabaseViewDialog : DialogFragment() {
         nameEditText.setText(currentFolderName)
 
         // Define an array of matrix cursor column names.
-        val matrixCursorColumnNames = arrayOf(BookmarksDatabaseHelper._ID, BookmarksDatabaseHelper.BOOKMARK_NAME)
+        val matrixCursorColumnNames = arrayOf(BookmarksDatabaseHelper.ID, BookmarksDatabaseHelper.BOOKMARK_NAME)
 
         // Create a matrix cursor.
         val matrixCursor = MatrixCursor(matrixCursorColumnNames)
index 6e48cfeab8f54e9ddfc7c543c8ba11febdf1782e..a386acba5fcdc35659c0fbfad97eeaebf4402042 100644 (file)
@@ -115,8 +115,8 @@ class EditBookmarkFolderDialog : DialogFragment() {
         // Convert the favorite icon byte array to a bitmap.
         val favoriteIconBitmap = BitmapFactory.decodeByteArray(favoriteIconByteArray, 0, favoriteIconByteArray.size)
 
-        // Initialize the database helper.  The `0` specifies a database version, but that is ignored and set instead using a constant in `BookmarksDatabaseHelper`.
-        bookmarksDatabaseHelper = BookmarksDatabaseHelper(context, null, null, 0)
+        // Initialize the database helper.
+        bookmarksDatabaseHelper = BookmarksDatabaseHelper(requireContext())
 
         // Get a cursor with the selected folder.
         val folderCursor = bookmarksDatabaseHelper.getBookmark(selectedFolderDatabaseId)
index 643e79cd01d5a3ff6c8883008d84d69f319231ac..101e6b3256585fb1344bf703e3130d241a28779d 100644 (file)
@@ -101,8 +101,8 @@ class MoveToFolderDialog : DialogFragment() {
         val currentFolder = requireArguments().getString(CURRENT_FOLDER)!!
         val selectedBookmarksLongArray = requireArguments().getLongArray(SELECTED_BOOKMARKS_LONG_ARRAY)!!
 
-        // Initialize the database helper.  The `0` specifies a database version, but that is ignored and set instead using a constant in the bookmarks database helper.
-        bookmarksDatabaseHelper = BookmarksDatabaseHelper(context, null, null, 0)
+        // Initialize the database helper.
+        bookmarksDatabaseHelper = BookmarksDatabaseHelper(requireContext())
 
         // Use an alert dialog builder to create the alert dialog.
         val dialogBuilder = AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog)
@@ -195,7 +195,7 @@ class MoveToFolderDialog : DialogFragment() {
             val homeFolderIconByteArray = homeFolderIconByteArrayOutputStream.toByteArray()
 
             // Setup the home folder matrix cursor column names.
-            val homeFolderMatrixCursorColumnNames = arrayOf(BookmarksDatabaseHelper._ID, BookmarksDatabaseHelper.BOOKMARK_NAME, BookmarksDatabaseHelper.FAVORITE_ICON)
+            val homeFolderMatrixCursorColumnNames = arrayOf(BookmarksDatabaseHelper.ID, BookmarksDatabaseHelper.BOOKMARK_NAME, BookmarksDatabaseHelper.FAVORITE_ICON)
 
             // Setup a matrix cursor for the `Home Folder`.
             val homeFolderMatrixCursor = MatrixCursor(homeFolderMatrixCursorColumnNames)
index 2d1cdd7d4798e5f2dd62dea89d3040d9838bb82a..56ec5192a2d4c35cb26b4e4075b8be6c122a6eda 100644 (file)
@@ -144,8 +144,8 @@ class PinnedMismatchDialog : DialogFragment() {
             val currentSslStartDateLong: Long = currentSslStartDate?.time ?: 0
             val currentSslEndDateLong: Long = currentSslEndDate?.time ?: 0
 
-            // Initialize the database handler.  The `0` specifies the database version, but that is ignored and set instead using a constant in the domains database helper.
-            val domainsDatabaseHelper = DomainsDatabaseHelper(context, null, null, 0)
+            // Initialize the database handler.
+            val domainsDatabaseHelper = DomainsDatabaseHelper(requireContext())
 
             // Update the SSL certificate if it is pinned.
             if (nestedScrollWebView.hasPinnedSslCertificate()) {
index 45e2b035a0d16c9f5a0e40e2d2141a63b3425f92..474d01c23b9c9c960fd2121db9bc08d454de4019 100644 (file)
@@ -201,8 +201,8 @@ public class DomainSettingsFragment extends Fragment {
         String startDateLabel = getString(R.string.start_date) + "  ";
         String endDateLabel = getString(R.string.end_date) + "  ";
 
-        // Initialize the database handler.  The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`.
-        DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(context, null, null, 0);
+        // Initialize the database handler.
+        DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(requireContext());
 
         // Get the database cursor for this ID and move it to the first row.
         Cursor domainCursor = domainsDatabaseHelper.getCursorForId(databaseId);
diff --git a/app/src/main/java/com/stoutner/privacybrowser/helpers/BookmarksDatabaseHelper.java b/app/src/main/java/com/stoutner/privacybrowser/helpers/BookmarksDatabaseHelper.java
deleted file mode 100644 (file)
index cceebf0..0000000
+++ /dev/null
@@ -1,802 +0,0 @@
-/*
- * Copyright © 2016-2022 Soren Stoutner <soren@stoutner.com>.
- *
- * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
- *
- * Privacy Browser Android is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Privacy Browser Android is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Privacy Browser Android.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.stoutner.privacybrowser.helpers;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteOpenHelper;
-
-public class BookmarksDatabaseHelper extends SQLiteOpenHelper {
-    private static final int SCHEMA_VERSION = 1;
-    static final String BOOKMARKS_DATABASE = "bookmarks.db";
-    static final String BOOKMARKS_TABLE = "bookmarks";
-
-    public static final String _ID = "_id";
-    public static final String BOOKMARK_NAME = "bookmarkname";
-    public static final String BOOKMARK_URL = "bookmarkurl";
-    public static final String PARENT_FOLDER = "parentfolder";
-    public static final String DISPLAY_ORDER = "displayorder";
-    public static final String IS_FOLDER = "isfolder";
-    public static final String FAVORITE_ICON = "favoriteicon";
-
-    static final String CREATE_BOOKMARKS_TABLE = "CREATE TABLE " + BOOKMARKS_TABLE + " (" +
-            _ID + " INTEGER PRIMARY KEY, " +
-            BOOKMARK_NAME + " TEXT, " +
-            BOOKMARK_URL + " TEXT, " +
-            PARENT_FOLDER + " TEXT, " +
-            DISPLAY_ORDER + " INTEGER, " +
-            IS_FOLDER + " BOOLEAN, " +
-            FAVORITE_ICON + " BLOB)";
-
-    // Initialize the database.  The lint warnings for the unused parameters are suppressed.
-    public BookmarksDatabaseHelper(Context context, @SuppressWarnings("UnusedParameters") String name, SQLiteDatabase.CursorFactory cursorFactory, @SuppressWarnings("UnusedParameters") int version) {
-        super(context, BOOKMARKS_DATABASE, cursorFactory, SCHEMA_VERSION);
-    }
-
-    @Override
-    public void onCreate(SQLiteDatabase bookmarksDatabase) {
-        // Create the bookmarks table.
-        bookmarksDatabase.execSQL(CREATE_BOOKMARKS_TABLE);
-    }
-
-    @Override
-    public void onUpgrade(SQLiteDatabase bookmarksDatabase, int oldVersion, int newVersion) {
-        // Code for upgrading the database will be added here when the schema version > 1.
-    }
-
-    // Create a bookmark.
-    public void createBookmark(String bookmarkName, String bookmarkURL, String parentFolder, int displayOrder, byte[] favoriteIcon) {
-        // Store the bookmark data in a `ContentValues`.
-        ContentValues bookmarkContentValues = new ContentValues();
-
-        // ID is created automatically.
-        bookmarkContentValues.put(BOOKMARK_NAME, bookmarkName);
-        bookmarkContentValues.put(BOOKMARK_URL, bookmarkURL);
-        bookmarkContentValues.put(PARENT_FOLDER, parentFolder);
-        bookmarkContentValues.put(DISPLAY_ORDER, displayOrder);
-        bookmarkContentValues.put(IS_FOLDER, false);
-        bookmarkContentValues.put(FAVORITE_ICON, favoriteIcon);
-
-        // Get a writable database handle.
-        SQLiteDatabase bookmarksDatabase = this.getWritableDatabase();
-
-        // Insert a new row.  The second argument is `null`, which makes it so that a completely null row cannot be created.
-        bookmarksDatabase.insert(BOOKMARKS_TABLE, null, bookmarkContentValues);
-
-        // Close the database handle.
-        bookmarksDatabase.close();
-    }
-
-    // Create a bookmark from content values.
-    void createBookmark(ContentValues contentValues) {
-        // Get a writable database.
-        SQLiteDatabase bookmarksDatabase = this.getWritableDatabase();
-
-        // Insert a new row.  The second argument is `null`, which makes it so that a completely null row cannot be created.
-        bookmarksDatabase.insert(BOOKMARKS_TABLE, null, contentValues);
-
-        // Close the database handle.
-        bookmarksDatabase.close();
-    }
-
-    // Create a folder.
-    public void createFolder(String folderName, String parentFolder, byte[] favoriteIcon) {
-        ContentValues bookmarkContentValues = new ContentValues();
-
-        // ID is created automatically.  Folders are always created at the top of the list.
-        bookmarkContentValues.put(BOOKMARK_NAME, folderName);
-        bookmarkContentValues.put(PARENT_FOLDER, parentFolder);
-        bookmarkContentValues.put(DISPLAY_ORDER, 0);
-        bookmarkContentValues.put(IS_FOLDER, true);
-        bookmarkContentValues.put(FAVORITE_ICON, favoriteIcon);
-
-        // Get a writable database handle.
-        SQLiteDatabase bookmarksDatabase = this.getWritableDatabase();
-
-        // The second argument is `null`, which makes it so that completely null rows cannot be created.  Not a problem in our case.
-        bookmarksDatabase.insert(BOOKMARKS_TABLE, null, bookmarkContentValues);
-
-        // Close the database handle.
-        bookmarksDatabase.close();
-    }
-
-    // Get a `Cursor` for the bookmark with the specified database ID.
-    public Cursor getBookmark(int databaseId) {
-        // Get a readable database handle.
-        SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
-
-        // Prepare the SQL statement to get the cursor for the database ID.
-        String GET_ONE_BOOKMARK = "SELECT * FROM " + BOOKMARKS_TABLE +
-                " WHERE " + _ID + " = " + databaseId;
-
-        // Return the results as a `Cursor`.  The second argument is `null` because there are no `selectionArgs`.  We can't close the `Cursor` because we need to use it in the parent activity.
-        return bookmarksDatabase.rawQuery(GET_ONE_BOOKMARK, null);
-    }
-
-    // Get the folder name for the specified database ID.
-    public String getFolderName (int databaseId) {
-        // Get a readable database handle.
-        SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
-
-        // Prepare the SQL statement to get the cursor for the folder.
-        String GET_FOLDER = "SELECT * FROM " + BOOKMARKS_TABLE +
-                " WHERE " + _ID + " = " + databaseId;
-
-        // Get a folder cursor.
-        Cursor folderCursor = bookmarksDatabase.rawQuery(GET_FOLDER, null);
-
-        // Get the folder name.
-        folderCursor.moveToFirst();
-        String folderName = folderCursor.getString(folderCursor.getColumnIndexOrThrow(BOOKMARK_NAME));
-
-        // Close the cursor and the database handle.
-        folderCursor.close();
-        bookmarksDatabase.close();
-
-        // Return the folder name.
-        return folderName;
-    }
-
-    // Get the database ID for the specified folder name.
-    public int getFolderDatabaseId (String folderName) {
-        // Get a readable database handle.
-        SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
-
-        // SQL escape `folderName`.
-        folderName = DatabaseUtils.sqlEscapeString(folderName);
-
-        // Prepare the SQL statement to get the `Cursor` for the folder.
-        String GET_FOLDER = "SELECT * FROM " + BOOKMARKS_TABLE +
-                " WHERE " + BOOKMARK_NAME + " = " + folderName +
-                " AND " + IS_FOLDER + " = " + 1;
-
-        // Get `folderCursor`.  The second argument is `null` because there are no `selectionArgs`.
-        Cursor folderCursor = bookmarksDatabase.rawQuery(GET_FOLDER, null);
-
-        // Get the database ID.
-        folderCursor.moveToFirst();
-        int databaseId = folderCursor.getInt(folderCursor.getColumnIndexOrThrow(_ID));
-
-        // Close the cursor and the database handle.
-        folderCursor.close();
-        bookmarksDatabase.close();
-
-        // Return the database ID.
-        return databaseId;
-    }
-
-    // Get a cursor for the specified folder name.
-    public Cursor getFolder(String folderName) {
-        // Get a readable database handle.
-        SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
-
-        // SQL escape `folderName`.
-        folderName = DatabaseUtils.sqlEscapeString(folderName);
-
-        // Prepare the SQL statement to get the `Cursor` for the folder.
-        String GET_FOLDER = "SELECT * FROM " + BOOKMARKS_TABLE +
-                " WHERE " + BOOKMARK_NAME + " = " + folderName +
-                " AND " + IS_FOLDER + " = " + 1;
-
-        // Return the results as a `Cursor`.  The second argument is `null` because there are no `selectionArgs`.
-        // We can't close the `Cursor` because we need to use it in the parent activity.
-        return bookmarksDatabase.rawQuery(GET_FOLDER, null);
-    }
-
-    // Get a cursor of all the folders except those specified.
-    public Cursor getFoldersExcept(String exceptFolders) {
-        // Get a readable database handle.
-        SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
-
-        // Prepare the SQL statement to get the `Cursor` for the folders.
-        String GET_FOLDERS_EXCEPT = "SELECT * FROM " + BOOKMARKS_TABLE +
-                " WHERE " + IS_FOLDER + " = " + 1 +
-                " AND " + BOOKMARK_NAME + " NOT IN (" + exceptFolders +
-                ") ORDER BY " + BOOKMARK_NAME + " ASC";
-
-        // Return the results as a `Cursor`.  The second argument is `null` because there are no `selectionArgs`.
-        // We can't close the `Cursor` because we need to use it in the parent activity.
-        return bookmarksDatabase.rawQuery(GET_FOLDERS_EXCEPT, null);
-    }
-
-    // Get a cursor with all the subfolders of the specified folder.
-    public Cursor getSubfolders(String currentFolder) {
-        // Get a readable database handle.
-        SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
-
-        // SQL escape `currentFolder.
-        currentFolder = DatabaseUtils.sqlEscapeString(currentFolder);
-
-        // Prepare the SQL statement to get the `Cursor` for the subfolders.
-        String GET_SUBFOLDERS = "SELECT * FROM " + BOOKMARKS_TABLE +
-                " WHERE " + PARENT_FOLDER + " = " + currentFolder +
-                " AND " + IS_FOLDER + " = " + 1;
-
-        // Return the results as a `Cursor`.  The second argument is `null` because there are no `selectionArgs`.
-        // We can't close the `Cursor` because we need to use it in the parent activity.
-        return bookmarksDatabase.rawQuery(GET_SUBFOLDERS, null);
-    }
-
-    // Get the name of the parent folder.
-    public String getParentFolderName(String currentFolder) {
-        // Get a readable database handle.
-        SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
-
-        // SQL escape `currentFolder`.
-        currentFolder = DatabaseUtils.sqlEscapeString(currentFolder);
-
-        // Prepare the SQL statement to get the current folder.
-        String GET_CURRENT_FOLDER = "SELECT * FROM " + BOOKMARKS_TABLE +
-                " WHERE " + IS_FOLDER + " = " + 1 +
-                " AND " + BOOKMARK_NAME + " = " + currentFolder;
-
-        // Get the bookmark cursor and move to the first entry.
-        Cursor bookmarkCursor = bookmarksDatabase.rawQuery(GET_CURRENT_FOLDER, null);
-        bookmarkCursor.moveToFirst();
-
-        // Store the name of the parent folder.
-        String parentFolder = bookmarkCursor.getString(bookmarkCursor.getColumnIndexOrThrow(PARENT_FOLDER));
-
-        // Close the cursor.
-        bookmarkCursor.close();
-
-        return parentFolder;
-    }
-
-    // Get the name of the parent folder.
-    public String getParentFolderName(int databaseId) {
-        // Get a readable database handle.
-        SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
-
-        // Prepare the SQL statement to get the current bookmark.
-        String GET_BOOKMARK = "SELECT * FROM " + BOOKMARKS_TABLE +
-                " WHERE " + _ID + " = " + databaseId;
-
-        // Get the bookmark cursor and move to the first entry.
-        Cursor bookmarkCursor = bookmarksDatabase.rawQuery(GET_BOOKMARK, null);
-        bookmarkCursor.moveToFirst();
-
-        // Store the name of the parent folder.
-        String parentFolder = bookmarkCursor.getString(bookmarkCursor.getColumnIndexOrThrow(PARENT_FOLDER));
-
-        // Close the cursor.
-        bookmarkCursor.close();
-
-        return parentFolder;
-    }
-
-    // Get a cursor of all the folders.
-    public Cursor getAllFolders() {
-        // Get a readable database handle.
-        SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
-
-        // Prepare the SQL statement to get the `Cursor` for all the folders.
-        String GET_ALL_FOLDERS = "SELECT * FROM " + BOOKMARKS_TABLE +
-                " WHERE " + IS_FOLDER + " = " + 1 +
-                " ORDER BY " + BOOKMARK_NAME + " ASC";
-
-        // Return the results as a cursor.  The cursor cannot be closed because it is used in the parent activity.
-        return bookmarksDatabase.rawQuery(GET_ALL_FOLDERS, null);
-    }
-
-    // Get a cursor for all bookmarks and folders.
-    public Cursor getAllBookmarks() {
-        // Get a readable database handle.
-        SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
-
-        // Get everything in the bookmarks table.
-        String GET_ALL_BOOKMARKS = "SELECT * FROM " + BOOKMARKS_TABLE;
-
-        // Return the result as a Cursor.  The Cursor cannot be closed because it is used in the parent activity.
-        return bookmarksDatabase.rawQuery(GET_ALL_BOOKMARKS, null);
-    }
-
-    // Get a cursor for all bookmarks and folders ordered by display order.
-    public Cursor getAllBookmarksByDisplayOrder() {
-        // Get a readable database handle.
-        SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
-
-        // Get everything in the bookmarks table ordered by display order.
-        String GET_ALL_BOOKMARKS = "SELECT * FROM " + BOOKMARKS_TABLE +
-                " ORDER BY " + DISPLAY_ORDER + " ASC";
-
-        // Return the result as a cursor.  The cursor cannot be closed because it is used in the parent activity.
-        return bookmarksDatabase.rawQuery(GET_ALL_BOOKMARKS, null);
-    }
-
-    // Get a cursor for all bookmarks and folders except those with the specified IDs.
-    public Cursor getAllBookmarksExcept(long[] exceptIdLongArray) {
-        // Get a readable database handle.
-        SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
-
-        // Prepare a string builder to contain the comma-separated list of IDs not to get.
-        StringBuilder idsNotToGetStringBuilder = new StringBuilder();
-
-        // Extract the array of IDs not to get to the string builder.
-        for (long databaseIdLong : exceptIdLongArray) {
-            // Check to see if there is already a number in the builder.
-            if (idsNotToGetStringBuilder.length() > 0) {
-                // This is not the first number, so place a `,` before the new number.
-                idsNotToGetStringBuilder.append(",");
-            }
-
-            // Add the new number to the builder.
-            idsNotToGetStringBuilder.append(databaseIdLong);
-        }
-
-        // Prepare the SQL statement to select all items except those with the specified IDs.
-        String GET_ALL_BOOKMARKS_EXCEPT_SPECIFIED = "SELECT * FROM " + BOOKMARKS_TABLE +
-                " WHERE " + _ID + " NOT IN (" + idsNotToGetStringBuilder.toString() + ")";
-
-        // Return the results as a cursor.  The cursor cannot be closed because it will be used in the parent activity.
-        return bookmarksDatabase.rawQuery(GET_ALL_BOOKMARKS_EXCEPT_SPECIFIED, null);
-    }
-
-    // Get a cursor for all bookmarks and folders by display order except for a specific of IDs.
-    public Cursor getAllBookmarksByDisplayOrderExcept(long[] exceptIdLongArray) {
-        // Get a readable database handle.
-        SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
-
-        // Prepare a string builder to contain the comma-separated list of IDs not to get.
-        StringBuilder idsNotToGetStringBuilder = new StringBuilder();
-
-        // Extract the array of IDs not to get to the string builder.
-        for (long databaseIdLong : exceptIdLongArray) {
-            // Check to see if there is already a number in the builder.
-            if (idsNotToGetStringBuilder.length() > 0) {
-                // This is not the first number, so place a `,` before the new number.
-                idsNotToGetStringBuilder.append(",");
-            }
-
-            // Add the new number to the builder.
-            idsNotToGetStringBuilder.append(databaseIdLong);
-        }
-
-        // Prepare the SQL statement to select all items except those with the specified IDs.
-        String GET_ALL_BOOKMARKS_EXCEPT_SPECIFIED = "SELECT * FROM " + BOOKMARKS_TABLE +
-                " WHERE " + _ID + " NOT IN (" + idsNotToGetStringBuilder.toString() +
-                ") ORDER BY " + DISPLAY_ORDER + " ASC";
-
-        // Return the results as a cursor.  The cursor cannot be closed because it will be used in the parent activity.
-        return bookmarksDatabase.rawQuery(GET_ALL_BOOKMARKS_EXCEPT_SPECIFIED, null);
-    }
-
-    // Get a cursor for bookmarks and folders in the specified folder.
-    public Cursor getBookmarks(String folderName) {
-        // Get a readable database handle.
-        SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
-
-        // SQL escape the folder name.
-        folderName = DatabaseUtils.sqlEscapeString(folderName);
-
-        // Get everything in the bookmarks table with `folderName` as the `PARENT_FOLDER`.
-        String GET_BOOKMARKS = "SELECT * FROM " + BOOKMARKS_TABLE +
-                " WHERE " + PARENT_FOLDER + " = " + folderName;
-
-        // Return the result as a cursor.  The cursor cannot be closed because it is used in the parent activity.
-        return bookmarksDatabase.rawQuery(GET_BOOKMARKS, null);
-    }
-
-    // Get a cursor for bookmarks and folders in the specified folder ordered by display order.
-    public Cursor getBookmarksByDisplayOrder(String folderName) {
-        // Get a readable database handle.
-        SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
-
-        // SQL escape `folderName`.
-        folderName = DatabaseUtils.sqlEscapeString(folderName);
-
-        // Get everything in the bookmarks table with `folderName` as the `PARENT_FOLDER`.
-        String GET_BOOKMARKS = "SELECT * FROM " + BOOKMARKS_TABLE +
-                " WHERE " + PARENT_FOLDER + " = " + folderName +
-                " ORDER BY " + DISPLAY_ORDER + " ASC";
-
-        // Return the result as a cursor.  The cursor cannot be closed because it is used in the parent activity.
-        return bookmarksDatabase.rawQuery(GET_BOOKMARKS, null);
-    }
-
-    // Get a cursor with just database ID of bookmarks and folders in the specified folder.  This is useful for deleting folders with bookmarks that have favorite icons too large to fit in a cursor.
-    public Cursor getBookmarkIds(String folderName) {
-        // Get a readable database handle.
-        SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
-
-        // SQL escape the folder name.
-        folderName = DatabaseUtils.sqlEscapeString(folderName);
-
-        // Get everything in the bookmarks table with `folderName` as the `PARENT_FOLDER`.
-        String GET_BOOKMARKS = "SELECT " + _ID + " FROM " + BOOKMARKS_TABLE +
-                " WHERE " + PARENT_FOLDER + " = " + folderName;
-
-        // Return the result as a cursor.  The cursor cannot be closed because it is used in the parent activity.
-        return bookmarksDatabase.rawQuery(GET_BOOKMARKS, null);
-    }
-
-    // Get a cursor for bookmarks and folders in the specified folder except for ta specific list of IDs.
-    public Cursor getBookmarksExcept(long[] exceptIdLongArray, String folderName) {
-        // Get a readable database handle.
-        SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
-
-        // Prepare a string builder to contain the comma-separated list of IDs not to get.
-        StringBuilder idsNotToGetStringBuilder = new StringBuilder();
-
-        // Extract the array of IDs not to get to the string builder.
-        for (long databaseIdLong : exceptIdLongArray) {
-            // Check to see if there is already a number in the builder.
-            if (idsNotToGetStringBuilder.length() > 0) {
-                // This is not the first number, so place a `,` before the new number.
-                idsNotToGetStringBuilder.append(",");
-            }
-
-            // Add the new number to the builder.
-            idsNotToGetStringBuilder.append(databaseIdLong);
-        }
-
-        // SQL escape the folder name.
-        folderName = DatabaseUtils.sqlEscapeString(folderName);
-
-        // Get everything in the bookmarks table with `folderName` as the `PARENT_FOLDER` except those with the specified IDs.
-        String GET_BOOKMARKS_EXCEPT_SPECIFIED = "SELECT * FROM " + BOOKMARKS_TABLE +
-                " WHERE " + PARENT_FOLDER + " = " + folderName +
-                " AND " + _ID + " NOT IN (" + idsNotToGetStringBuilder.toString() + ")";
-
-        // Return the result as a cursor.  The cursor cannot be closed because it is used in the parent activity.
-        return bookmarksDatabase.rawQuery(GET_BOOKMARKS_EXCEPT_SPECIFIED, null);
-    }
-
-    // Get a cursor for bookmarks and folders in the specified folder by display order except for a specific list of IDs.
-    public Cursor getBookmarksByDisplayOrderExcept(long[] exceptIdLongArray, String folderName) {
-        // Get a readable database handle.
-        SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
-
-        // Prepare a string builder to contain the comma-separated list of IDs not to get.
-        StringBuilder idsNotToGetStringBuilder = new StringBuilder();
-
-        // Extract the array of IDs not to get to the string builder.
-        for (long databaseIdLong : exceptIdLongArray) {
-            // Check to see if there is already a number in the builder.
-            if (idsNotToGetStringBuilder.length() > 0) {
-                // This is not the first number, so place a `,` before the new number.
-                idsNotToGetStringBuilder.append(",");
-            }
-
-            // Add the new number to the builder.
-            idsNotToGetStringBuilder.append(databaseIdLong);
-        }
-
-        // SQL escape `folderName`.
-        folderName = DatabaseUtils.sqlEscapeString(folderName);
-
-        // Prepare the SQL statement to select all items except those with the specified IDs.
-        String GET_BOOKMARKS_EXCEPT_SPECIFIED = "SELECT * FROM " + BOOKMARKS_TABLE +
-                " WHERE " + PARENT_FOLDER + " = " + folderName +
-                " AND " + _ID + " NOT IN (" + idsNotToGetStringBuilder.toString() +
-                ") ORDER BY " + DISPLAY_ORDER + " ASC";
-
-        // Return the results as a cursor.  The cursor cannot be closed because it will be used in the parent activity.
-        return bookmarksDatabase.rawQuery(GET_BOOKMARKS_EXCEPT_SPECIFIED, null);
-    }
-
-    // Check if a database ID is a folder.
-    public boolean isFolder(int databaseId) {
-        // Get a readable database handle.
-        SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
-
-        // Prepare the SQL statement to determine if `databaseId` is a folder.
-        String CHECK_IF_FOLDER = "SELECT " + IS_FOLDER + " FROM " + BOOKMARKS_TABLE +
-                " WHERE " + _ID + " = " + databaseId;
-
-        // Populate the folder cursor.
-        Cursor folderCursor = bookmarksDatabase.rawQuery(CHECK_IF_FOLDER, null);
-
-        // Ascertain if this database ID is a folder.
-        folderCursor.moveToFirst();
-        boolean isFolder = (folderCursor.getInt(folderCursor.getColumnIndexOrThrow(IS_FOLDER)) == 1);
-
-        // Close the cursor and the database handle.
-        folderCursor.close();
-        bookmarksDatabase.close();
-
-        return isFolder;
-    }
-
-    // Update the bookmark name and URL.
-    public void updateBookmark(int databaseId, String bookmarkName, String bookmarkUrl) {
-        // Initialize a ContentValues.
-        ContentValues bookmarkContentValues = new ContentValues();
-
-        // Store the updated values.
-        bookmarkContentValues.put(BOOKMARK_NAME, bookmarkName);
-        bookmarkContentValues.put(BOOKMARK_URL, bookmarkUrl);
-
-        // Get a writable database handle.
-        SQLiteDatabase bookmarksDatabase = this.getWritableDatabase();
-
-        // Update the bookmark.  The last argument is `null` because there are no `whereArgs`.
-        bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, _ID + " = " + databaseId, null);
-
-        // Close the database handle.
-        bookmarksDatabase.close();
-    }
-
-    // Update the bookmark name, URL, parent folder, and display order.
-    public void updateBookmark(int databaseId, String bookmarkName, String bookmarkUrl, String parentFolder, int displayOrder) {
-        // Initialize a `ContentValues`.
-        ContentValues bookmarkContentValues = new ContentValues();
-
-        // Store the updated values.
-        bookmarkContentValues.put(BOOKMARK_NAME, bookmarkName);
-        bookmarkContentValues.put(BOOKMARK_URL, bookmarkUrl);
-        bookmarkContentValues.put(PARENT_FOLDER, parentFolder);
-        bookmarkContentValues.put(DISPLAY_ORDER, displayOrder);
-
-        // Get a writable database handle.
-        SQLiteDatabase bookmarksDatabase = this.getWritableDatabase();
-
-        // Update the bookmark.  The last argument is `null` because there are no `whereArgs`.
-        bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, _ID + " = " + databaseId, null);
-
-        // Close the database handle.
-        bookmarksDatabase.close();
-    }
-
-    // Update the bookmark name, URL, and favorite icon.
-    public void updateBookmark(int databaseId, String bookmarkName, String bookmarkUrl, byte[] favoriteIcon) {
-        // Initialize a `ContentValues`.
-        ContentValues bookmarkContentValues = new ContentValues();
-
-        // Store the updated values.
-        bookmarkContentValues.put(BOOKMARK_NAME, bookmarkName);
-        bookmarkContentValues.put(BOOKMARK_URL, bookmarkUrl);
-        bookmarkContentValues.put(FAVORITE_ICON, favoriteIcon);
-
-        // Get a writable database handle.
-        SQLiteDatabase bookmarksDatabase = this.getWritableDatabase();
-
-        // Update the bookmark.  The last argument is `null` because there are no `whereArgs`.
-        bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, _ID + " = " + databaseId, null);
-
-        // Close the database handle.
-        bookmarksDatabase.close();
-    }
-
-    // Update the bookmark name, URL, parent folder, display order, and favorite icon.
-    public void updateBookmark(int databaseId, String bookmarkName, String bookmarkUrl, String parentFolder, int displayOrder, byte[] favoriteIcon) {
-        // Initialize a `ContentValues`.
-        ContentValues bookmarkContentValues = new ContentValues();
-
-        // Store the updated values.
-        bookmarkContentValues.put(BOOKMARK_NAME, bookmarkName);
-        bookmarkContentValues.put(BOOKMARK_URL, bookmarkUrl);
-        bookmarkContentValues.put(PARENT_FOLDER, parentFolder);
-        bookmarkContentValues.put(DISPLAY_ORDER, displayOrder);
-        bookmarkContentValues.put(FAVORITE_ICON, favoriteIcon);
-
-        // Get a writable database handle.
-        SQLiteDatabase bookmarksDatabase = this.getWritableDatabase();
-
-        // Update the bookmark.  The last argument is `null` because there are no `whereArgs`.
-        bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, _ID + " = " + databaseId, null);
-
-        // Close the database handle.
-        bookmarksDatabase.close();
-    }
-
-    // Update the folder name.
-    public void updateFolder(int databaseId, String oldFolderName, String newFolderName) {
-        // Get a writable database handle.
-        SQLiteDatabase bookmarksDatabase = this.getWritableDatabase();
-
-        // Update the folder first.  Store the new folder name in `folderContentValues`.
-        ContentValues folderContentValues = new ContentValues();
-        folderContentValues.put(BOOKMARK_NAME, newFolderName);
-
-        // Run the update on the folder.  The last argument is `null` because there are no `whereArgs`.
-        bookmarksDatabase.update(BOOKMARKS_TABLE, folderContentValues, _ID + " = " + databaseId, null);
-
-        // Update the bookmarks inside the folder.  Store the new parent folder name in `bookmarkContentValues`.
-        ContentValues bookmarkContentValues = new ContentValues();
-        bookmarkContentValues.put(PARENT_FOLDER, newFolderName);
-
-        // SQL escape `oldFolderName`.
-        oldFolderName = DatabaseUtils.sqlEscapeString(oldFolderName);
-
-        // Run the update on all the bookmarks that currently list `oldFolderName` as their parent folder.  The last argument is `null` because there are no `whereArgs`.
-        bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, PARENT_FOLDER + " = " + oldFolderName, null);
-
-        // Close the database handle.
-        bookmarksDatabase.close();
-    }
-
-    // Update the folder icon.
-    public void updateFolder(int databaseId, byte[] folderIcon) {
-        // Get a writable database handle.
-        SQLiteDatabase bookmarksDatabase = this.getWritableDatabase();
-
-        // Store the updated icon in `folderContentValues`.
-        ContentValues folderContentValues = new ContentValues();
-        folderContentValues.put(FAVORITE_ICON, folderIcon);
-
-        // Run the update on the folder.  The last argument is `null` because there are no `whereArgs`.
-        bookmarksDatabase.update(BOOKMARKS_TABLE, folderContentValues, _ID + " = " + databaseId, null);
-
-        // Close the database handle.
-        bookmarksDatabase.close();
-    }
-
-    // Update the folder name, parent folder, and display order.
-    public void updateFolder(int databaseId, String oldFolderName, String newFolderName, String parentFolder, int displayOrder) {
-        // Get a writable database handle.
-        SQLiteDatabase bookmarksDatabase = this.getWritableDatabase();
-
-        // Update the folder first.  Store the new folder name in `folderContentValues`.
-        ContentValues folderContentValues = new ContentValues();
-        folderContentValues.put(BOOKMARK_NAME, newFolderName);
-        folderContentValues.put(PARENT_FOLDER, parentFolder);
-        folderContentValues.put(DISPLAY_ORDER, displayOrder);
-
-        // Run the update on the folder.  The last argument is `null` because there are no `whereArgs`.
-        bookmarksDatabase.update(BOOKMARKS_TABLE, folderContentValues, _ID + " = " + databaseId, null);
-
-        // Update the bookmarks inside the folder.  Store the new parent folder name in `bookmarkContentValues`.
-        ContentValues bookmarkContentValues = new ContentValues();
-        bookmarkContentValues.put(PARENT_FOLDER, newFolderName);
-
-        // SQL escape `oldFolderName`.
-        oldFolderName = DatabaseUtils.sqlEscapeString(oldFolderName);
-
-        // Run the update on all the bookmarks that currently list `oldFolderName` as their parent folder.  The last argument is `null` because there are no `whereArgs`.
-        bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, PARENT_FOLDER + " = " + oldFolderName, null);
-
-        // Close the database handle.
-        bookmarksDatabase.close();
-    }
-
-    // Update the folder name and icon.
-    public void updateFolder(int databaseId, String oldFolderName, String newFolderName, byte[] folderIcon) {
-        // Get a writable database handle.
-        SQLiteDatabase bookmarksDatabase = this.getWritableDatabase();
-
-        // Update the folder first.  Store the updated values in `folderContentValues`.
-        ContentValues folderContentValues = new ContentValues();
-        folderContentValues.put(BOOKMARK_NAME, newFolderName);
-        folderContentValues.put(FAVORITE_ICON, folderIcon);
-
-        // Run the update on the folder.  The last argument is `null` because there are no `whereArgs`.
-        bookmarksDatabase.update(BOOKMARKS_TABLE, folderContentValues, _ID + " = " + databaseId, null);
-
-        // Update the bookmarks inside the folder.  Store the new parent folder name in `bookmarkContentValues`.
-        ContentValues bookmarkContentValues = new ContentValues();
-        bookmarkContentValues.put(PARENT_FOLDER, newFolderName);
-
-        // SQL escape `oldFolderName`.
-        oldFolderName = DatabaseUtils.sqlEscapeString(oldFolderName);
-
-        // Run the update on all the bookmarks that currently list `oldFolderName` as their parent folder.  The last argument is `null` because there are no `whereArgs`.
-        bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, PARENT_FOLDER + " = " + oldFolderName, null);
-
-        // Close the database handle.
-        bookmarksDatabase.close();
-    }
-
-    // Update the folder name and icon.
-    public void updateFolder(int databaseId, String oldFolderName, String newFolderName, String parentFolder, int displayOrder, byte[] folderIcon) {
-        // Get a writable database handle.
-        SQLiteDatabase bookmarksDatabase = this.getWritableDatabase();
-
-        // Update the folder first.  Store the updated values in `folderContentValues`.
-        ContentValues folderContentValues = new ContentValues();
-        folderContentValues.put(BOOKMARK_NAME, newFolderName);
-        folderContentValues.put(PARENT_FOLDER, parentFolder);
-        folderContentValues.put(DISPLAY_ORDER, displayOrder);
-        folderContentValues.put(FAVORITE_ICON, folderIcon);
-
-        // Run the update on the folder.  The last argument is `null` because there are no `whereArgs`.
-        bookmarksDatabase.update(BOOKMARKS_TABLE, folderContentValues, _ID + " = " + databaseId, null);
-
-        // Update the bookmarks inside the folder.  Store the new parent folder name in `bookmarkContentValues`.
-        ContentValues bookmarkContentValues = new ContentValues();
-        bookmarkContentValues.put(PARENT_FOLDER, newFolderName);
-
-        // SQL escape `oldFolderName`.
-        oldFolderName = DatabaseUtils.sqlEscapeString(oldFolderName);
-
-        // Run the update on all the bookmarks that currently list `oldFolderName` as their parent folder.  The last argument is `null` because there are no `whereArgs`.
-        bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, PARENT_FOLDER + " = " + oldFolderName, null);
-
-        // Close the database handle.
-        bookmarksDatabase.close();
-    }
-
-    // Update the display order for one bookmark or folder.
-    public void updateDisplayOrder(int databaseId, int displayOrder) {
-        // Get a writable database handle.
-        SQLiteDatabase bookmarksDatabase = this.getWritableDatabase();
-
-        // Store the new display order in `bookmarkContentValues`.
-        ContentValues bookmarkContentValues = new ContentValues();
-        bookmarkContentValues.put(DISPLAY_ORDER, displayOrder);
-
-        // Update the database.  The last argument is `null` because there are no `whereArgs`.
-        bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, _ID + " = " + databaseId, null);
-
-        // Close the database handle.
-        bookmarksDatabase.close();
-    }
-
-    // Move one bookmark or folder to a new folder.
-    public void moveToFolder(int databaseId, String newFolder) {
-        // Get a writable database handle.
-        SQLiteDatabase bookmarksDatabase = this.getWritableDatabase();
-
-        // SQL escape the new folder name.
-        String newFolderSqlEscaped = DatabaseUtils.sqlEscapeString(newFolder);
-
-        // Prepare a SQL query to select all the bookmarks in the new folder.
-        String NEW_FOLDER = "SELECT * FROM " + BOOKMARKS_TABLE +
-                " WHERE " + PARENT_FOLDER + " = " + newFolderSqlEscaped +
-                " ORDER BY " + DISPLAY_ORDER + " ASC";
-
-        // Get a cursor for all the bookmarks in the new folder.  The second argument is `null` because there are no `selectionArgs`.
-        Cursor newFolderCursor = bookmarksDatabase.rawQuery(NEW_FOLDER, null);
-
-        // Instantiate a variable to store the display order after the move.
-        int displayOrder;
-
-        // Set the new display order.
-        if (newFolderCursor.getCount() > 0) {  // There are already bookmarks in the folder.
-            // Move to the last bookmark.
-            newFolderCursor.moveToLast();
-
-            // Set the display order to be one greater that the last bookmark.
-            displayOrder = newFolderCursor.getInt(newFolderCursor.getColumnIndexOrThrow(DISPLAY_ORDER)) + 1;
-        } else {  // There are no bookmarks in the new folder.
-            // Set the display order to be `0`.
-            displayOrder = 0;
-        }
-
-        // Close the new folder `Cursor`.
-        newFolderCursor.close();
-
-        // Store the new values in `bookmarkContentValues`.
-        ContentValues bookmarkContentValues = new ContentValues();
-        bookmarkContentValues.put(DISPLAY_ORDER, displayOrder);
-        bookmarkContentValues.put(PARENT_FOLDER, newFolder);
-
-        // Update the database.  The last argument is `null` because there are no `whereArgs`.
-        bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, _ID + " = " + databaseId, null);
-
-        // Close the database handle.
-        bookmarksDatabase.close();
-    }
-
-    // Delete one bookmark.
-    public void deleteBookmark(int databaseId) {
-        // Get a writable database handle.
-        SQLiteDatabase bookmarksDatabase = this.getWritableDatabase();
-
-        // Deletes the row with the given `databaseId`.  The last argument is `null` because we don't need additional parameters.
-        bookmarksDatabase.delete(BOOKMARKS_TABLE, _ID + " = " + databaseId, null);
-
-        // Close the database handle.
-        bookmarksDatabase.close();
-    }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/stoutner/privacybrowser/helpers/BookmarksDatabaseHelper.kt b/app/src/main/java/com/stoutner/privacybrowser/helpers/BookmarksDatabaseHelper.kt
new file mode 100644 (file)
index 0000000..12753e1
--- /dev/null
@@ -0,0 +1,748 @@
+/*
+ * Copyright © 2016-2022 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
+ *
+ * Privacy Browser Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Privacy Browser Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Privacy Browser Android.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.stoutner.privacybrowser.helpers
+
+import android.content.ContentValues
+import android.content.Context
+import android.database.Cursor
+import android.database.DatabaseUtils
+import android.database.sqlite.SQLiteDatabase
+import android.database.sqlite.SQLiteOpenHelper
+
+// The private 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.
+        const val BOOKMARKS_DATABASE = "bookmarks.db"
+        const val BOOKMARKS_TABLE = "bookmarks"
+
+        // The schema constants.
+        const val ID = "_id"
+        const val BOOKMARK_NAME = "bookmarkname"
+        const val BOOKMARK_URL = "bookmarkurl"
+        const val PARENT_FOLDER = "parentfolder"
+        const val DISPLAY_ORDER = "displayorder"
+        const val IS_FOLDER = "isfolder"
+        const val FAVORITE_ICON = "favoriteicon"
+
+        // The table creation constant.
+        const val CREATE_BOOKMARKS_TABLE = "CREATE TABLE " + BOOKMARKS_TABLE + " (" +
+                ID + " INTEGER PRIMARY KEY, " +
+                BOOKMARK_NAME + " TEXT, " +
+                BOOKMARK_URL + " TEXT, " +
+                PARENT_FOLDER + " TEXT, " +
+                DISPLAY_ORDER + " INTEGER, " +
+                IS_FOLDER + " BOOLEAN, " +
+                FAVORITE_ICON + " BLOB)"
+    }
+
+    override fun onCreate(bookmarksDatabase: SQLiteDatabase) {
+        // Create the bookmarks table.
+        bookmarksDatabase.execSQL(CREATE_BOOKMARKS_TABLE)
+    }
+
+    override fun onUpgrade(bookmarksDatabase: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
+        // Code for upgrading the database will be added here when the schema version > 1.
+    }
+
+    // Create a bookmark.
+    fun createBookmark(bookmarkName: String, bookmarkURL: String, parentFolder: String, displayOrder: Int, favoriteIcon: ByteArray) {
+        // Store the bookmark data in a content values.
+        val bookmarkContentValues = ContentValues()
+
+        // The ID is created automatically.
+        bookmarkContentValues.put(BOOKMARK_NAME, bookmarkName)
+        bookmarkContentValues.put(BOOKMARK_URL, bookmarkURL)
+        bookmarkContentValues.put(PARENT_FOLDER, parentFolder)
+        bookmarkContentValues.put(DISPLAY_ORDER, displayOrder)
+        bookmarkContentValues.put(IS_FOLDER, false)
+        bookmarkContentValues.put(FAVORITE_ICON, favoriteIcon)
+
+        // Get a writable database handle.
+        val bookmarksDatabase = this.writableDatabase
+
+        // Insert a new row.
+        bookmarksDatabase.insert(BOOKMARKS_TABLE, null, bookmarkContentValues)
+
+        // Close the database handle.
+        bookmarksDatabase.close()
+    }
+
+    // Create a bookmark from content values.
+    fun createBookmark(contentValues: ContentValues) {
+        // Get a writable database.
+        val bookmarksDatabase = this.writableDatabase
+
+        // Insert a new row.
+        bookmarksDatabase.insert(BOOKMARKS_TABLE, null, contentValues)
+
+        // Close the database handle.
+        bookmarksDatabase.close()
+    }
+
+    // Create a folder.
+    fun createFolder(folderName: String, parentFolder: String, favoriteIcon: ByteArray) {
+        // Store the bookmark folder data in a content values.
+        val bookmarkFolderContentValues = ContentValues()
+
+        // The ID is created automatically.  Folders are always created at the top of the list.
+        bookmarkFolderContentValues.put(BOOKMARK_NAME, folderName)
+        bookmarkFolderContentValues.put(PARENT_FOLDER, parentFolder)
+        bookmarkFolderContentValues.put(DISPLAY_ORDER, 0)
+        bookmarkFolderContentValues.put(IS_FOLDER, true)
+        bookmarkFolderContentValues.put(FAVORITE_ICON, favoriteIcon)
+
+        // Get a writable database handle.
+        val bookmarksDatabase = this.writableDatabase
+
+        // Insert the new folder.
+        bookmarksDatabase.insert(BOOKMARKS_TABLE, null, bookmarkFolderContentValues)
+
+        // Close the database handle.
+        bookmarksDatabase.close()
+    }
+
+    // Get a cursor for the bookmark with the specified database ID.
+    fun getBookmark(databaseId: Int): Cursor {
+        // Get a readable database handle.
+        val bookmarksDatabase = this.readableDatabase
+
+        // Return the cursor for the database ID.  The cursor can't be closed because it is used in the parent activity.
+        return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $ID = $databaseId", null)
+    }
+
+    // Get the folder name for the specified database ID.
+    fun getFolderName(databaseId: Int): String {
+        // Get a readable database handle.
+        val bookmarksDatabase = this.readableDatabase
+
+        // Get the cursor for the folder with the specified database ID.
+        val folderCursor = bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $ID = $databaseId", null)
+
+        // Move to the first record.
+        folderCursor.moveToFirst()
+
+        // Get the folder name.
+        val folderName = folderCursor.getString(folderCursor.getColumnIndexOrThrow(BOOKMARK_NAME))
+
+        // Close the cursor and the database handle.
+        folderCursor.close()
+        bookmarksDatabase.close()
+
+        // Return the folder name.
+        return folderName
+    }
+
+    // Get the database ID for the specified folder name.
+    fun getFolderDatabaseId(folderName: String): Int {
+        // Get a readable database handle.
+        val bookmarksDatabase = this.readableDatabase
+
+        // SQL escape the folder name.
+        val sqlEscapedFolderName = DatabaseUtils.sqlEscapeString(folderName)
+
+        // Get the cursor for the folder with the specified name.
+        val folderCursor = bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $BOOKMARK_NAME = $sqlEscapedFolderName AND $IS_FOLDER = 1", null)
+
+        // Move to the first record.
+        folderCursor.moveToFirst()
+
+        // Get the database ID.
+        val databaseId = folderCursor.getInt(folderCursor.getColumnIndexOrThrow(ID))
+
+        // Close the cursor and the database handle.
+        folderCursor.close()
+        bookmarksDatabase.close()
+
+        // Return the database ID.
+        return databaseId
+    }
+
+    // Get a cursor for the specified folder name.
+    fun getFolder(folderName: String): Cursor {
+        // Get a readable database handle.
+        val bookmarksDatabase = this.readableDatabase
+
+        // SQL escape the folder name.
+        val sqlEscapedFolderName = DatabaseUtils.sqlEscapeString(folderName)
+
+        // Return the cursor for the specified folder.  The cursor can't be closed because it is used in the parent activity.
+        return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $BOOKMARK_NAME = $sqlEscapedFolderName AND $IS_FOLDER = 1", null)
+    }
+
+    // Get a cursor of all the folders except those specified.
+    fun getFoldersExcept(exceptFolders: String): Cursor {
+        // Get a readable database handle.
+        val bookmarksDatabase = this.readableDatabase
+
+        // Return the cursor of all folders except those specified.  Each individual folder in the list has already been SQL escaped.  The cursor can't be closed because it is used in the parent activity.
+        return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $IS_FOLDER = 1 AND $BOOKMARK_NAME NOT IN ($exceptFolders) ORDER BY $BOOKMARK_NAME ASC", null)
+    }
+
+    // Get a cursor with all the subfolders of the specified folder.
+    fun getSubfolders(currentFolder: String): Cursor {
+        // Get a readable database handle.
+        val bookmarksDatabase = this.readableDatabase
+
+        // SQL escape the current folder.
+        val sqlEscapedCurrentFolder = DatabaseUtils.sqlEscapeString(currentFolder)
+
+        // Return the cursor with the subfolders.  The cursor can't be closed because it is used in the parent activity.
+        return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $PARENT_FOLDER = $sqlEscapedCurrentFolder AND $IS_FOLDER = 1", null)
+    }
+
+    // Get the name of the parent folder.
+    fun getParentFolderName(currentFolder: String): String {
+        // Get a readable database handle.
+        val bookmarksDatabase = this.readableDatabase
+
+        // SQL escape the current folder.
+        val sqlEscapedCurrentFolder = DatabaseUtils.sqlEscapeString(currentFolder)
+
+        // Get a cursor for the current folder.
+        val bookmarkCursor = bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $IS_FOLDER = 1 AND $BOOKMARK_NAME = $sqlEscapedCurrentFolder", null)
+
+        // Move to the first record.
+        bookmarkCursor.moveToFirst()
+
+        // Store the name of the parent folder.
+        val parentFolder = bookmarkCursor.getString(bookmarkCursor.getColumnIndexOrThrow(PARENT_FOLDER))
+
+        // Close the cursor and the database.
+        bookmarkCursor.close()
+        bookmarksDatabase.close()
+
+        // Return the parent folder string.
+        return parentFolder
+    }
+
+    // Get the name of the parent folder.
+    fun getParentFolderName(databaseId: Int): String {
+        // Get a readable database handle.
+        val bookmarksDatabase = this.readableDatabase
+
+        // Get a cursor for the specified database ID.
+        val bookmarkCursor = bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $ID = $databaseId", null)
+
+        // Move to the first record.
+        bookmarkCursor.moveToFirst()
+
+        // Store the name of the parent folder.
+        val parentFolder = bookmarkCursor.getString(bookmarkCursor.getColumnIndexOrThrow(PARENT_FOLDER))
+
+        // Close the cursor and the database.
+        bookmarkCursor.close()
+        bookmarksDatabase.close()
+
+        // Return the parent folder string.
+        return parentFolder
+    }
+
+    // Get a cursor of all the folders.
+    val allFolders: Cursor
+        get() {
+            // Get a readable database handle.
+            val bookmarksDatabase = this.readableDatabase
+
+            // Return the cursor with the all the folders.  The cursor cannot be closed because it is used in the parent activity.
+            return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $IS_FOLDER = 1 ORDER BY $BOOKMARK_NAME ASC", null)
+        }
+
+    // Get a cursor for all bookmarks and folders.
+    val allBookmarks: Cursor
+        get() {
+            // Get a readable database handle.
+            val bookmarksDatabase = this.readableDatabase
+
+            // Return a cursor with the entire contents of the bookmarks table.  The cursor cannot be closed because it is used in the parent activity.
+            return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE", null)
+        }
+
+    // Get a cursor for all bookmarks and folders ordered by display order.
+    val allBookmarksByDisplayOrder: Cursor
+        get() {
+            // Get a readable database handle.
+            val bookmarksDatabase = this.readableDatabase
+
+            // Return a cursor with the entire contents of the bookmarks table ordered by the display order.  The cursor cannot be closed because it is used in the parent activity.
+            return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE ORDER BY $DISPLAY_ORDER ASC", null)
+        }
+
+    // Get a cursor for all bookmarks and folders except those with the specified IDs.
+    fun getAllBookmarksExcept(exceptIdLongArray: LongArray): Cursor {
+        // Get a readable database handle.
+        val bookmarksDatabase = this.readableDatabase
+
+        // Prepare a string builder to contain the comma-separated list of IDs not to get.
+        val idsNotToGetStringBuilder = StringBuilder()
+
+        // Extract the array of IDs not to get to the string builder.
+        for (databaseIdLong in exceptIdLongArray) {
+            // Check to see if there is already a number in the builder.
+            if (idsNotToGetStringBuilder.isNotEmpty()) {
+                // This is not the first number, so place a `,` before the new number.
+                idsNotToGetStringBuilder.append(",")
+            }
+
+            // Add the new number to the builder.
+            idsNotToGetStringBuilder.append(databaseIdLong)
+        }
+
+        // Return a cursor with all the bookmarks except those specified.  The cursor cannot be closed because it is used in the parent activity.
+        return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $ID NOT IN ($idsNotToGetStringBuilder)", null)
+    }
+
+    // Get a cursor for all bookmarks and folders by display order except those with the specified IDs.
+    fun getAllBookmarksByDisplayOrderExcept(exceptIdLongArray: LongArray): Cursor {
+        // Get a readable database handle.
+        val bookmarksDatabase = this.readableDatabase
+
+        // Prepare a string builder to contain the comma-separated list of IDs not to get.
+        val idsNotToGetStringBuilder = StringBuilder()
+
+        // Extract the array of IDs not to get to the string builder.
+        for (databaseIdLong in exceptIdLongArray) {
+            // Check to see if there is already a number in the builder.
+            if (idsNotToGetStringBuilder.isNotEmpty()) {
+                // This is not the first number, so place a `,` before the new number.
+                idsNotToGetStringBuilder.append(",")
+            }
+
+            // Add the new number to the builder.
+            idsNotToGetStringBuilder.append(databaseIdLong)
+        }
+
+        // Return a cursor with all the bookmarks except those specified ordered by display order.  The cursor cannot be closed because it is used in the parent activity.
+        return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $ID NOT IN ($idsNotToGetStringBuilder) ORDER BY $DISPLAY_ORDER ASC", null)
+    }
+
+    // Get a cursor for bookmarks and folders in the specified folder.
+    fun getBookmarks(folderName: String): Cursor {
+        // Get a readable database handle.
+        val bookmarksDatabase = this.readableDatabase
+
+        // SQL escape the folder name.
+        val sqlEscapedFolderName = DatabaseUtils.sqlEscapeString(folderName)
+
+        // Return a cursor with all the bookmarks in a specified folder.  The cursor cannot be closed because it is used in the parent activity.
+        return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $PARENT_FOLDER = $sqlEscapedFolderName", null)
+    }
+
+    // Get a cursor for bookmarks and folders in the specified folder ordered by display order.
+    fun getBookmarksByDisplayOrder(folderName: String): Cursor {
+        // Get a readable database handle.
+        val bookmarksDatabase = this.readableDatabase
+
+        // SQL escape the folder name.
+        val sqlEscapedFolderName = DatabaseUtils.sqlEscapeString(folderName)
+
+        // Return a cursor with all the bookmarks in the specified folder ordered by display order.  The cursor cannot be closed because it is used in the parent activity.
+        return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $PARENT_FOLDER = $sqlEscapedFolderName ORDER BY $DISPLAY_ORDER ASC", null)
+    }
+
+    // Get a cursor with just database ID of bookmarks and folders in the specified folder.  This is useful for deleting folders with bookmarks that have favorite icons too large to fit in a cursor.
+    fun getBookmarkIds(folderName: String): Cursor {
+        // Get a readable database handle.
+        val bookmarksDatabase = this.readableDatabase
+
+        // SQL escape the folder name.
+        val sqlEscapedFolderName = DatabaseUtils.sqlEscapeString(folderName)
+
+        // Return a cursor with all the database IDs.  The cursor cannot be closed because it is used in the parent activity.
+        return bookmarksDatabase.rawQuery("SELECT $ID FROM $BOOKMARKS_TABLE WHERE $PARENT_FOLDER = $sqlEscapedFolderName", null)
+    }
+
+    // Get a cursor for bookmarks and folders in the specified folder except those with the specified IDs.
+    fun getBookmarksExcept(exceptIdLongArray: LongArray, folderName: String): Cursor {
+        // Get a readable database handle.
+        val bookmarksDatabase = this.readableDatabase
+
+        // Prepare a string builder to contain the comma-separated list of IDs not to get.
+        val idsNotToGetStringBuilder = StringBuilder()
+
+        // Extract the array of IDs not to get to the string builder.
+        for (databaseIdLong in exceptIdLongArray) {
+            // Check to see if there is already a number in the builder.
+            if (idsNotToGetStringBuilder.isNotEmpty()) {
+                // This is not the first number, so place a `,` before the new number.
+                idsNotToGetStringBuilder.append(",")
+            }
+
+            // Add the new number to the builder.
+            idsNotToGetStringBuilder.append(databaseIdLong)
+        }
+
+        // SQL escape the folder name.
+        val sqlEscapedFolderName = DatabaseUtils.sqlEscapeString(folderName)
+
+        // Return a cursor with all the bookmarks in the specified folder except for those database IDs specified.  The cursor cannot be closed because it is used in the parent activity.
+        return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $PARENT_FOLDER = $sqlEscapedFolderName AND $ID NOT IN ($idsNotToGetStringBuilder)", null)
+    }
+
+    // Get a cursor for bookmarks and folders in the specified folder by display order except those with the specified IDs.
+    fun getBookmarksByDisplayOrderExcept(exceptIdLongArray: LongArray, folderName: String): Cursor {
+        // Get a readable database handle.
+        val bookmarksDatabase = this.readableDatabase
+
+        // Prepare a string builder to contain the comma-separated list of IDs not to get.
+        val idsNotToGetStringBuilder = StringBuilder()
+
+        // Extract the array of IDs not to get to the string builder.
+        for (databaseIdLong in exceptIdLongArray) {
+            // Check to see if there is already a number in the builder.
+            if (idsNotToGetStringBuilder.isNotEmpty()) {
+                // This is not the first number, so place a `,` before the new number.
+                idsNotToGetStringBuilder.append(",")
+            }
+
+            // Add the new number to the builder.
+            idsNotToGetStringBuilder.append(databaseIdLong)
+        }
+
+        // SQL escape the folder name.
+        val sqlEscapedFolderName = DatabaseUtils.sqlEscapeString(folderName)
+
+        // Return a cursor with all the bookmarks in the specified folder except for those database IDs specified ordered by display order.
+        // The cursor cannot be closed because it will be used in the parent activity.
+        return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $PARENT_FOLDER = $sqlEscapedFolderName AND $ID NOT IN ($idsNotToGetStringBuilder) ORDER BY $DISPLAY_ORDER ASC",
+            null)
+    }
+
+    // Check if a database ID is a folder.
+    fun isFolder(databaseId: Int): Boolean {
+        // Get a readable database handle.
+        val bookmarksDatabase = this.readableDatabase
+
+        // Get a cursor with the is folder field for the specified database ID.
+        val folderCursor = bookmarksDatabase.rawQuery("SELECT $IS_FOLDER FROM $BOOKMARKS_TABLE WHERE $ID = $databaseId", null)
+
+        // Move to the first record.
+        folderCursor.moveToFirst()
+
+        // Ascertain if this database ID is a folder.
+        val isFolder = folderCursor.getInt(folderCursor.getColumnIndexOrThrow(IS_FOLDER)) == 1
+
+        // Close the cursor and the database handle.
+        folderCursor.close()
+        bookmarksDatabase.close()
+
+        // Return the folder status.
+        return isFolder
+    }
+
+    // Update the bookmark name and URL.
+    fun updateBookmark(databaseId: Int, bookmarkName: String, bookmarkUrl: String) {
+        // Initialize a content values.
+        val bookmarkContentValues = ContentValues()
+
+        // Store the updated values.
+        bookmarkContentValues.put(BOOKMARK_NAME, bookmarkName)
+        bookmarkContentValues.put(BOOKMARK_URL, bookmarkUrl)
+
+        // Get a writable database handle.
+        val bookmarksDatabase = this.writableDatabase
+
+        // Update the bookmark.
+        bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, "$ID = $databaseId", null)
+
+        // Close the database handle.
+        bookmarksDatabase.close()
+    }
+
+    // Update the bookmark name, URL, parent folder, and display order.
+    fun updateBookmark(databaseId: Int, bookmarkName: String, bookmarkUrl: String, parentFolder: String, displayOrder: Int) {
+        // Initialize a content values.
+        val bookmarkContentValues = ContentValues()
+
+        // Store the updated values.
+        bookmarkContentValues.put(BOOKMARK_NAME, bookmarkName)
+        bookmarkContentValues.put(BOOKMARK_URL, bookmarkUrl)
+        bookmarkContentValues.put(PARENT_FOLDER, parentFolder)
+        bookmarkContentValues.put(DISPLAY_ORDER, displayOrder)
+
+        // Get a writable database handle.
+        val bookmarksDatabase = this.writableDatabase
+
+        // Update the bookmark.
+        bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, "$ID = $databaseId", null)
+
+        // Close the database handle.
+        bookmarksDatabase.close()
+    }
+
+    // Update the bookmark name, URL, and favorite icon.
+    fun updateBookmark(databaseId: Int, bookmarkName: String, bookmarkUrl: String, favoriteIcon: ByteArray) {
+        // Initialize a content values.
+        val bookmarkContentValues = ContentValues()
+
+        // Store the updated values.
+        bookmarkContentValues.put(BOOKMARK_NAME, bookmarkName)
+        bookmarkContentValues.put(BOOKMARK_URL, bookmarkUrl)
+        bookmarkContentValues.put(FAVORITE_ICON, favoriteIcon)
+
+        // Get a writable database handle.
+        val bookmarksDatabase = this.writableDatabase
+
+        // Update the bookmark.
+        bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, "$ID = $databaseId", null)
+
+        // Close the database handle.
+        bookmarksDatabase.close()
+    }
+
+    // Update the bookmark name, URL, parent folder, display order, and favorite icon.
+    fun updateBookmark(databaseId: Int, bookmarkName: String, bookmarkUrl: String, parentFolder: String, displayOrder: Int, favoriteIcon: ByteArray) {
+        // Initialize a content values.
+        val bookmarkContentValues = ContentValues()
+
+        // Store the updated values.
+        bookmarkContentValues.put(BOOKMARK_NAME, bookmarkName)
+        bookmarkContentValues.put(BOOKMARK_URL, bookmarkUrl)
+        bookmarkContentValues.put(PARENT_FOLDER, parentFolder)
+        bookmarkContentValues.put(DISPLAY_ORDER, displayOrder)
+        bookmarkContentValues.put(FAVORITE_ICON, favoriteIcon)
+
+        // Get a writable database handle.
+        val bookmarksDatabase = this.writableDatabase
+
+        // Update the bookmark.
+        bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, "$ID = $databaseId", null)
+
+        // Close the database handle.
+        bookmarksDatabase.close()
+    }
+
+    // Update the folder name.
+    fun updateFolder(databaseId: Int, oldFolderName: String, newFolderName: String) {
+        // Get a writable database handle.
+        val bookmarksDatabase = this.writableDatabase
+
+        // Create a folder content values.
+        val folderContentValues = ContentValues()
+
+        // Store the new folder name.
+        folderContentValues.put(BOOKMARK_NAME, newFolderName)
+
+        // Run the update on the folder.
+        bookmarksDatabase.update(BOOKMARKS_TABLE, folderContentValues, "$ID = $databaseId", null)
+
+        // Create a bookmark content values.
+        val bookmarkContentValues = ContentValues()
+
+        // Store the new parent folder name.
+        bookmarkContentValues.put(PARENT_FOLDER, newFolderName)
+
+        // SQL escape the old folder name.
+        val sqlEscapedOldFolderName = DatabaseUtils.sqlEscapeString(oldFolderName)
+
+        // Run the update on all the bookmarks that currently list the old folder name as their parent folder.
+        bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, "$PARENT_FOLDER = $sqlEscapedOldFolderName", null)
+
+        // Close the database handle.
+        bookmarksDatabase.close()
+    }
+
+    // Update the folder icon.
+    fun updateFolder(databaseId: Int, folderIcon: ByteArray) {
+        // Get a writable database handle.
+        val bookmarksDatabase = this.writableDatabase
+
+        // Create a content values.
+        val folderContentValues = ContentValues()
+
+        // Store the updated icon.
+        folderContentValues.put(FAVORITE_ICON, folderIcon)
+
+        // Run the update on the folder.
+        bookmarksDatabase.update(BOOKMARKS_TABLE, folderContentValues, "$ID = $databaseId", null)
+
+        // Close the database handle.
+        bookmarksDatabase.close()
+    }
+
+    // Update the folder name, parent folder, and display order.
+    fun updateFolder(databaseId: Int, oldFolderName: String, newFolderName: String, parentFolder: String, displayOrder: Int) {
+        // Get a writable database handle.
+        val bookmarksDatabase = this.writableDatabase
+
+        // Create a folder content values.
+        val folderContentValues = ContentValues()
+
+        // Store the new folder values.
+        folderContentValues.put(BOOKMARK_NAME, newFolderName)
+        folderContentValues.put(PARENT_FOLDER, parentFolder)
+        folderContentValues.put(DISPLAY_ORDER, displayOrder)
+
+        // Run the update on the folder.
+        bookmarksDatabase.update(BOOKMARKS_TABLE, folderContentValues, "$ID = $databaseId", null)
+
+        // Create a bookmark content values.
+        val bookmarkContentValues = ContentValues()
+
+        // Store the new parent folder name.
+        bookmarkContentValues.put(PARENT_FOLDER, newFolderName)
+
+        // SQL escape the old folder name.
+        val sqlEscapedOldFolderName = DatabaseUtils.sqlEscapeString(oldFolderName)
+
+        // Run the update on all the bookmarks that currently list the old folder name as their parent folder.
+        bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, "$PARENT_FOLDER = $sqlEscapedOldFolderName", null)
+
+        // Close the database handle.
+        bookmarksDatabase.close()
+    }
+
+    // Update the folder name and icon.
+    fun updateFolder(databaseId: Int, oldFolderName: String, newFolderName: String, folderIcon: ByteArray) {
+        // Get a writable database handle.
+        val bookmarksDatabase = this.writableDatabase
+
+        // Create a folder content values.
+        val folderContentValues = ContentValues()
+
+        // Store the updated values.
+        folderContentValues.put(BOOKMARK_NAME, newFolderName)
+        folderContentValues.put(FAVORITE_ICON, folderIcon)
+
+        // Run the update on the folder.
+        bookmarksDatabase.update(BOOKMARKS_TABLE, folderContentValues, "$ID = $databaseId", null)
+
+        // Create a bookmark content values.
+        val bookmarkContentValues = ContentValues()
+
+        // Store the new parent folder name.
+        bookmarkContentValues.put(PARENT_FOLDER, newFolderName)
+
+        // SQL escape the old folder name.
+        val sqlEscapedOldFolderName = DatabaseUtils.sqlEscapeString(oldFolderName)
+
+        // Run the update on all the bookmarks that currently list the old folder name as their parent folder.
+        bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, "$PARENT_FOLDER = $sqlEscapedOldFolderName", null)
+
+        // Close the database handle.
+        bookmarksDatabase.close()
+    }
+
+    // Update the folder name and icon.
+    fun updateFolder(databaseId: Int, oldFolderName: String, newFolderName: String, parentFolder: String, displayOrder: Int, folderIcon: ByteArray) {
+        // Get a writable database handle.
+        val bookmarksDatabase = this.writableDatabase
+
+        // Create a folder content values.
+        val folderContentValues = ContentValues()
+
+        // Store the updated values.
+        folderContentValues.put(BOOKMARK_NAME, newFolderName)
+        folderContentValues.put(PARENT_FOLDER, parentFolder)
+        folderContentValues.put(DISPLAY_ORDER, displayOrder)
+        folderContentValues.put(FAVORITE_ICON, folderIcon)
+
+        // Run the update on the folder.
+        bookmarksDatabase.update(BOOKMARKS_TABLE, folderContentValues, "$ID = $databaseId", null)
+
+        // Create a bookmark content values.
+        val bookmarkContentValues = ContentValues()
+
+        // Store the new parent folder name.
+        bookmarkContentValues.put(PARENT_FOLDER, newFolderName)
+
+        // SQL escape the old folder name.
+        val sqlEscapedOldFolderName = DatabaseUtils.sqlEscapeString(oldFolderName)
+
+        // Run the update on all the bookmarks that currently list the old folder name as their parent folder.
+        bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, "$PARENT_FOLDER = $sqlEscapedOldFolderName", null)
+
+        // Close the database handle.
+        bookmarksDatabase.close()
+    }
+
+    // Update the display order for one bookmark or folder.
+    fun updateDisplayOrder(databaseId: Int, displayOrder: Int) {
+        // Get a writable database handle.
+        val bookmarksDatabase = this.writableDatabase
+
+        // Create a content values.
+        val bookmarkContentValues = ContentValues()
+
+        // Store the new display order.
+        bookmarkContentValues.put(DISPLAY_ORDER, displayOrder)
+
+        // Update the database.
+        bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, "$ID = $databaseId", null)
+
+        // Close the database handle.
+        bookmarksDatabase.close()
+    }
+
+    // Move one bookmark or folder to a new folder.
+    fun moveToFolder(databaseId: Int, newFolder: String) {
+        // Get a writable database handle.
+        val bookmarksDatabase = this.writableDatabase
+
+        // SQL escape the new folder name.
+        val sqlEscapedNewFolder = DatabaseUtils.sqlEscapeString(newFolder)
+
+        // Get a cursor for all the bookmarks in the new folder ordered by display order.
+        val newFolderCursor = bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $PARENT_FOLDER = $sqlEscapedNewFolder ORDER BY $DISPLAY_ORDER ASC", null)
+
+        // Set the new display order.
+        val displayOrder: Int = if (newFolderCursor.count > 0) {  // There are already bookmarks in the folder.
+            // Move to the last bookmark.
+            newFolderCursor.moveToLast()
+
+            // Set the display order to be one greater that the last bookmark.
+            newFolderCursor.getInt(newFolderCursor.getColumnIndexOrThrow(DISPLAY_ORDER)) + 1
+        } else {  // There are no bookmarks in the new folder.
+            // Set the display order to be `0`.
+            0
+        }
+
+        // Close the cursor.
+        newFolderCursor.close()
+
+        // Create a content values.
+        val bookmarkContentValues = ContentValues()
+
+        // Store the new values.
+        bookmarkContentValues.put(DISPLAY_ORDER, displayOrder)
+        bookmarkContentValues.put(PARENT_FOLDER, newFolder)
+
+        // Update the database.
+        bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, "$ID = $databaseId", null)
+
+        // Close the database handle.
+        bookmarksDatabase.close()
+    }
+
+    // Delete one bookmark.
+    fun deleteBookmark(databaseId: Int) {
+        // Get a writable database handle.
+        val bookmarksDatabase = this.writableDatabase
+
+        // Deletes the row with the given database ID.
+        bookmarksDatabase.delete(BOOKMARKS_TABLE, "$ID = $databaseId", null)
+
+        // Close the database handle.
+        bookmarksDatabase.close()
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/stoutner/privacybrowser/helpers/CheckPinnedMismatchHelper.java b/app/src/main/java/com/stoutner/privacybrowser/helpers/CheckPinnedMismatchHelper.java
deleted file mode 100644 (file)
index 011b783..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright © 2018-2019,2021-2022 Soren Stoutner <soren@stoutner.com>.
- *
- * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
- *
- * Privacy Browser Android is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Privacy Browser Android is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Privacy Browser Android.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.stoutner.privacybrowser.helpers;
-
-import android.app.Activity;
-import android.net.http.SslCertificate;
-
-import androidx.fragment.app.DialogFragment;
-import androidx.fragment.app.FragmentManager;
-
-import com.stoutner.privacybrowser.R;
-import com.stoutner.privacybrowser.activities.MainWebViewActivity;
-import com.stoutner.privacybrowser.dataclasses.PendingDialog;
-import com.stoutner.privacybrowser.dialogs.PinnedMismatchDialog;
-import com.stoutner.privacybrowser.views.NestedScrollWebView;
-
-import java.util.ArrayList;
-import java.util.Date;
-
-public class CheckPinnedMismatchHelper {
-    public static void checkPinnedMismatch(Activity activity, FragmentManager fragmentManager, NestedScrollWebView nestedScrollWebView) {
-        // Initialize the current SSL certificate variables.
-        String currentWebsiteIssuedToCName = "";
-        String currentWebsiteIssuedToOName = "";
-        String currentWebsiteIssuedToUName = "";
-        String currentWebsiteIssuedByCName = "";
-        String currentWebsiteIssuedByOName = "";
-        String currentWebsiteIssuedByUName = "";
-        Date currentWebsiteSslStartDate = null;
-        Date currentWebsiteSslEndDate = null;
-
-        // Initialize the pinned SSL certificate variables.
-        String pinnedSslIssuedToCName = "";
-        String pinnedSslIssuedToOName = "";
-        String pinnedSslIssuedToUName = "";
-        String pinnedSslIssuedByCName = "";
-        String pinnedSslIssuedByOName = "";
-        String pinnedSslIssuedByUName = "";
-        Date pinnedSslStartDate = null;
-        Date pinnedSslEndDate = null;
-
-        // Get the current website SSL certificate.
-        SslCertificate currentWebsiteSslCertificate = nestedScrollWebView.getCertificate();
-
-        // Extract the individual pieces of information from the current website SSL certificate if it is not null.
-        if (currentWebsiteSslCertificate != null) {
-            currentWebsiteIssuedToCName = currentWebsiteSslCertificate.getIssuedTo().getCName();
-            currentWebsiteIssuedToOName = currentWebsiteSslCertificate.getIssuedTo().getOName();
-            currentWebsiteIssuedToUName = currentWebsiteSslCertificate.getIssuedTo().getUName();
-            currentWebsiteIssuedByCName = currentWebsiteSslCertificate.getIssuedBy().getCName();
-            currentWebsiteIssuedByOName = currentWebsiteSslCertificate.getIssuedBy().getOName();
-            currentWebsiteIssuedByUName = currentWebsiteSslCertificate.getIssuedBy().getUName();
-            currentWebsiteSslStartDate = currentWebsiteSslCertificate.getValidNotBeforeDate();
-            currentWebsiteSslEndDate = currentWebsiteSslCertificate.getValidNotAfterDate();
-        }
-
-        // Get the pinned SSL certificate information if it exists.
-        if (nestedScrollWebView.hasPinnedSslCertificate()) {
-            // Get the pinned SSL certificate.
-            ArrayList<Object> pinnedSslCertificateArrayList = nestedScrollWebView.getPinnedSslCertificate();
-
-            // Extract the arrays from the array list.
-            String[] pinnedSslCertificateStringArray = (String[]) pinnedSslCertificateArrayList.get(0);
-            Date[] pinnedSslCertificateDateArray = (Date[]) pinnedSslCertificateArrayList.get(1);
-
-            // Populate the pinned SSL certificate string variables.
-            pinnedSslIssuedToCName = pinnedSslCertificateStringArray[0];
-            pinnedSslIssuedToOName = pinnedSslCertificateStringArray[1];
-            pinnedSslIssuedToUName = pinnedSslCertificateStringArray[2];
-            pinnedSslIssuedByCName = pinnedSslCertificateStringArray[3];
-            pinnedSslIssuedByOName = pinnedSslCertificateStringArray[4];
-            pinnedSslIssuedByUName = pinnedSslCertificateStringArray[5];
-
-            // Populate the pinned SSL certificate date variables.
-            pinnedSslStartDate = pinnedSslCertificateDateArray[0];
-            pinnedSslEndDate = pinnedSslCertificateDateArray[1];
-        }
-
-        // Initialize string variables to store the SSL certificate dates.  Strings are needed to compare the values below, which doesn't work with dates if the first one is null.
-        String currentWebsiteSslStartDateString = "";
-        String currentWebsiteSslEndDateString = "";
-        String pinnedSslStartDateString = "";
-        String pinnedSslEndDateString = "";
-
-        // Convert the dates to strings if they are not null.
-        if (currentWebsiteSslStartDate != null) {
-            currentWebsiteSslStartDateString = currentWebsiteSslStartDate.toString();
-        }
-
-        if (currentWebsiteSslEndDate != null) {
-            currentWebsiteSslEndDateString = currentWebsiteSslEndDate.toString();
-        }
-
-        if (pinnedSslStartDate != null) {
-            pinnedSslStartDateString = pinnedSslStartDate.toString();
-        }
-
-        if (pinnedSslEndDate != null) {
-            pinnedSslEndDateString = pinnedSslEndDate.toString();
-        }
-
-        // Check to see if the pinned information matches the current information.
-        if ((!nestedScrollWebView.getPinnedIpAddresses().equals("") && !nestedScrollWebView.getCurrentIpAddresses().equals(nestedScrollWebView.getPinnedIpAddresses())) ||
-                (nestedScrollWebView.hasPinnedSslCertificate() && (!currentWebsiteIssuedToCName.equals(pinnedSslIssuedToCName) ||
-                !currentWebsiteIssuedToOName.equals(pinnedSslIssuedToOName) || !currentWebsiteIssuedToUName.equals(pinnedSslIssuedToUName) ||
-                !currentWebsiteIssuedByCName.equals(pinnedSslIssuedByCName) || !currentWebsiteIssuedByOName.equals(pinnedSslIssuedByOName) ||
-                !currentWebsiteIssuedByUName.equals(pinnedSslIssuedByUName) || !currentWebsiteSslStartDateString.equals(pinnedSslStartDateString) ||
-                !currentWebsiteSslEndDateString.equals(pinnedSslEndDateString)))) {
-
-            // Get a handle for the pinned mismatch alert dialog.
-            DialogFragment pinnedMismatchDialogFragment = PinnedMismatchDialog.displayDialog(nestedScrollWebView.getWebViewFragmentId());
-
-            // Try to show the dialog.  Sometimes the window is not active.
-            try {
-                // Show the pinned mismatch alert dialog.
-                pinnedMismatchDialogFragment.show(fragmentManager, activity.getString(R.string.pinned_mismatch));
-            } catch (Exception exception) {
-                // Add the dialog to the pending dialog array list.  It will be displayed in `onStart()`.
-                MainWebViewActivity.pendingDialogsArrayList.add(new PendingDialog(pinnedMismatchDialogFragment, activity.getString(R.string.pinned_mismatch)));
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/stoutner/privacybrowser/helpers/CheckPinnedMismatchHelper.kt b/app/src/main/java/com/stoutner/privacybrowser/helpers/CheckPinnedMismatchHelper.kt
new file mode 100644 (file)
index 0000000..8fa8369
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * Copyright © 2018-2019,2021-2022 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
+ *
+ * Privacy Browser Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Privacy Browser Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Privacy Browser Android.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.stoutner.privacybrowser.helpers
+
+import android.app.Activity
+
+import androidx.fragment.app.DialogFragment
+import androidx.fragment.app.FragmentManager
+
+import com.stoutner.privacybrowser.R
+import com.stoutner.privacybrowser.activities.MainWebViewActivity
+import com.stoutner.privacybrowser.dataclasses.PendingDialog
+import com.stoutner.privacybrowser.dialogs.PinnedMismatchDialog.Companion.displayDialog
+import com.stoutner.privacybrowser.views.NestedScrollWebView
+
+import java.lang.Exception
+
+import java.util.Date
+
+object CheckPinnedMismatchHelper {
+    @JvmStatic
+    fun checkPinnedMismatch(activity: Activity, fragmentManager: FragmentManager, nestedScrollWebView: NestedScrollWebView) {
+        // Initialize the current SSL certificate variables.
+        var currentWebsiteIssuedToCName = ""
+        var currentWebsiteIssuedToOName = ""
+        var currentWebsiteIssuedToUName = ""
+        var currentWebsiteIssuedByCName = ""
+        var currentWebsiteIssuedByOName = ""
+        var currentWebsiteIssuedByUName = ""
+        var currentWebsiteSslStartDate: Date? = null
+        var currentWebsiteSslEndDate: Date? = null
+
+        // Initialize the pinned SSL certificate variables.
+        var pinnedSslIssuedToCName = ""
+        var pinnedSslIssuedToOName = ""
+        var pinnedSslIssuedToUName = ""
+        var pinnedSslIssuedByCName = ""
+        var pinnedSslIssuedByOName = ""
+        var pinnedSslIssuedByUName = ""
+        var pinnedSslStartDate: Date? = null
+        var pinnedSslEndDate: Date? = null
+
+        // Get the current website SSL certificate.
+        val currentWebsiteSslCertificate = nestedScrollWebView.certificate
+
+        // Extract the individual pieces of information from the current website SSL certificate if it is not null.
+        if (currentWebsiteSslCertificate != null) {
+            currentWebsiteIssuedToCName = currentWebsiteSslCertificate.issuedTo.cName
+            currentWebsiteIssuedToOName = currentWebsiteSslCertificate.issuedTo.oName
+            currentWebsiteIssuedToUName = currentWebsiteSslCertificate.issuedTo.uName
+            currentWebsiteIssuedByCName = currentWebsiteSslCertificate.issuedBy.cName
+            currentWebsiteIssuedByOName = currentWebsiteSslCertificate.issuedBy.oName
+            currentWebsiteIssuedByUName = currentWebsiteSslCertificate.issuedBy.uName
+            currentWebsiteSslStartDate = currentWebsiteSslCertificate.validNotBeforeDate
+            currentWebsiteSslEndDate = currentWebsiteSslCertificate.validNotAfterDate
+        }
+
+        // Get the pinned SSL certificate information if it exists.
+        if (nestedScrollWebView.hasPinnedSslCertificate()) {
+            // Get the pinned SSL certificate.
+            val pinnedSslCertificatePair = nestedScrollWebView.getPinnedSslCertificate()
+
+            // Extract the arrays from the array list.
+            val pinnedSslCertificateStringArray = pinnedSslCertificatePair.first
+            val pinnedSslCertificateDateArray = pinnedSslCertificatePair.second
+
+            // Populate the pinned SSL certificate string variables.
+            pinnedSslIssuedToCName = pinnedSslCertificateStringArray[0]
+            pinnedSslIssuedToOName = pinnedSslCertificateStringArray[1]
+            pinnedSslIssuedToUName = pinnedSslCertificateStringArray[2]
+            pinnedSslIssuedByCName = pinnedSslCertificateStringArray[3]
+            pinnedSslIssuedByOName = pinnedSslCertificateStringArray[4]
+            pinnedSslIssuedByUName = pinnedSslCertificateStringArray[5]
+
+            // Populate the pinned SSL certificate date variables.
+            pinnedSslStartDate = pinnedSslCertificateDateArray[0]
+            pinnedSslEndDate = pinnedSslCertificateDateArray[1]
+        }
+
+        // Initialize string variables to store the SSL certificate dates.  Strings are needed to compare the values below, which doesn't work with dates if the first one is null.
+        var currentWebsiteSslStartDateString = ""
+        var currentWebsiteSslEndDateString = ""
+        var pinnedSslStartDateString = ""
+        var pinnedSslEndDateString = ""
+
+        // Convert the dates to strings if they are not null.
+        if (currentWebsiteSslStartDate != null) {
+            currentWebsiteSslStartDateString = currentWebsiteSslStartDate.toString()
+        }
+        if (currentWebsiteSslEndDate != null) {
+            currentWebsiteSslEndDateString = currentWebsiteSslEndDate.toString()
+        }
+        if (pinnedSslStartDate != null) {
+            pinnedSslStartDateString = pinnedSslStartDate.toString()
+        }
+        if (pinnedSslEndDate != null) {
+            pinnedSslEndDateString = pinnedSslEndDate.toString()
+        }
+
+        // Check to see if the pinned information matches the current information.
+        if (((nestedScrollWebView.pinnedIpAddresses != "") && (nestedScrollWebView.currentIpAddresses != nestedScrollWebView.pinnedIpAddresses)) ||
+            (nestedScrollWebView.hasPinnedSslCertificate() && ((currentWebsiteIssuedToCName != pinnedSslIssuedToCName) ||
+                    (currentWebsiteIssuedToOName != pinnedSslIssuedToOName) || (currentWebsiteIssuedToUName != pinnedSslIssuedToUName) ||
+                    (currentWebsiteIssuedByCName != pinnedSslIssuedByCName) || (currentWebsiteIssuedByOName != pinnedSslIssuedByOName) ||
+                    (currentWebsiteIssuedByUName != pinnedSslIssuedByUName) || (currentWebsiteSslStartDateString != pinnedSslStartDateString) ||
+                    (currentWebsiteSslEndDateString != pinnedSslEndDateString)))) {
+            // Get a handle for the pinned mismatch alert dialog.
+            val pinnedMismatchDialogFragment: DialogFragment = displayDialog(nestedScrollWebView.webViewFragmentId)
+
+            // Try to show the dialog.  Sometimes the window is not active.
+            try {
+                // Show the pinned mismatch alert dialog.
+                pinnedMismatchDialogFragment.show(fragmentManager, activity.getString(R.string.pinned_mismatch))
+            } catch (exception: Exception) {
+                // Add the dialog to the pending dialog array list.  It will be displayed in `onStart()`.
+                MainWebViewActivity.pendingDialogsArrayList.add(PendingDialog(pinnedMismatchDialogFragment, activity.getString(R.string.pinned_mismatch)))
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/stoutner/privacybrowser/helpers/DomainsDatabaseHelper.java b/app/src/main/java/com/stoutner/privacybrowser/helpers/DomainsDatabaseHelper.java
deleted file mode 100644 (file)
index 2455fb3..0000000
+++ /dev/null
@@ -1,462 +0,0 @@
-/*
- * Copyright © 2017-2022 Soren Stoutner <soren@stoutner.com>.
- *
- * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
- *
- * Privacy Browser Android is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Privacy Browser Android is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Privacy Browser Android.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.stoutner.privacybrowser.helpers;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteOpenHelper;
-import android.preference.PreferenceManager;
-
-import com.stoutner.privacybrowser.R;
-
-public class DomainsDatabaseHelper extends SQLiteOpenHelper {
-    private static final int SCHEMA_VERSION = 13;
-    static final String DOMAINS_DATABASE = "domains.db";
-    static final String DOMAINS_TABLE = "domains";
-
-    public static final String _ID = "_id";
-    public static final String DOMAIN_NAME = "domainname";
-    public static final String ENABLE_JAVASCRIPT = "enablejavascript";
-    public static final String COOKIES = "cookies";
-    public static final String ENABLE_DOM_STORAGE = "enabledomstorage";
-    public static final String ENABLE_FORM_DATA = "enableformdata";  // Form data can be removed once the minimum API >= 26.
-    public static final String ENABLE_EASYLIST = "enableeasylist";
-    public static final String ENABLE_EASYPRIVACY = "enableeasyprivacy";
-    public static final String ENABLE_FANBOYS_ANNOYANCE_LIST = "enablefanboysannoyancelist";
-    public static final String ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST = "enablefanboyssocialblockinglist";
-    public static final String ULTRALIST = "ultralist";
-    public static final String ENABLE_ULTRAPRIVACY = "enableultraprivacy";
-    public static final String BLOCK_ALL_THIRD_PARTY_REQUESTS = "blockallthirdpartyrequests";
-    public static final String USER_AGENT = "useragent";
-    public static final String FONT_SIZE = "fontsize";
-    public static final String SWIPE_TO_REFRESH = "swipetorefresh";
-    public static final String WEBVIEW_THEME = "webview_theme";
-    public static final String WIDE_VIEWPORT = "wide_viewport";
-    public static final String DISPLAY_IMAGES = "displayimages";
-    public static final String PINNED_SSL_CERTIFICATE = "pinnedsslcertificate";
-    public static final String SSL_ISSUED_TO_COMMON_NAME = "sslissuedtocommonname";
-    public static final String SSL_ISSUED_TO_ORGANIZATION = "sslissuedtoorganization";
-    public static final String SSL_ISSUED_TO_ORGANIZATIONAL_UNIT = "sslissuedtoorganizationalunit";
-    public static final String SSL_ISSUED_BY_COMMON_NAME = "sslissuedbycommonname";
-    public static final String SSL_ISSUED_BY_ORGANIZATION = "sslissuedbyorganization";
-    public static final String SSL_ISSUED_BY_ORGANIZATIONAL_UNIT = "sslissuedbyorganizationalunit";
-    public static final String SSL_START_DATE = "sslstartdate";
-    public static final String SSL_END_DATE = "sslenddate";
-    public static final String PINNED_IP_ADDRESSES = "pinned_ip_addresses";
-    public static final String IP_ADDRESSES = "ip_addresses";
-
-    // Spinner constants.
-    public static final int SYSTEM_DEFAULT = 0;
-    public static final int ENABLED = 1;
-    public static final int DISABLED = 2;
-    public static final int LIGHT_THEME = 1;
-    public static final int DARK_THEME = 2;
-
-    static final String CREATE_DOMAINS_TABLE = "CREATE TABLE " + DOMAINS_TABLE + " (" +
-            _ID + " INTEGER PRIMARY KEY, " +
-            DOMAIN_NAME + " TEXT, " +
-            ENABLE_JAVASCRIPT + " BOOLEAN, " +
-            COOKIES + " BOOLEAN, " +
-            ENABLE_DOM_STORAGE + " BOOLEAN, " +
-            ENABLE_FORM_DATA + " BOOLEAN, " +
-            ENABLE_EASYLIST + " BOOLEAN, " +
-            ENABLE_EASYPRIVACY + " BOOLEAN, " +
-            ENABLE_FANBOYS_ANNOYANCE_LIST + " BOOLEAN, " +
-            ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST + " BOOLEAN, " +
-            ULTRALIST + " BOOLEAN, " +
-            ENABLE_ULTRAPRIVACY + " BOOLEAN, " +
-            BLOCK_ALL_THIRD_PARTY_REQUESTS + " BOOLEAN, " +
-            USER_AGENT + " TEXT, " +
-            FONT_SIZE + " INTEGER, " +
-            SWIPE_TO_REFRESH + " INTEGER, " +
-            WEBVIEW_THEME + " INTEGER, " +
-            WIDE_VIEWPORT + " INTEGER, " +
-            DISPLAY_IMAGES + " INTEGER, " +
-            PINNED_SSL_CERTIFICATE + " BOOLEAN, " +
-            SSL_ISSUED_TO_COMMON_NAME + " TEXT, " +
-            SSL_ISSUED_TO_ORGANIZATION + " TEXT, " +
-            SSL_ISSUED_TO_ORGANIZATIONAL_UNIT + " TEXT, " +
-            SSL_ISSUED_BY_COMMON_NAME + " TEXT, " +
-            SSL_ISSUED_BY_ORGANIZATION + " TEXT, " +
-            SSL_ISSUED_BY_ORGANIZATIONAL_UNIT + " TEXT, " +
-            SSL_START_DATE + " INTEGER, " +
-            SSL_END_DATE + " INTEGER, " +
-            PINNED_IP_ADDRESSES + " BOOLEAN, " +
-            IP_ADDRESSES + " TEXT)";
-
-    private final Context appContext;
-
-    // Initialize the database.  The lint warnings for the unused parameters are suppressed.
-    public DomainsDatabaseHelper(Context context, @SuppressWarnings("UnusedParameters") String name, SQLiteDatabase.CursorFactory cursorFactory, @SuppressWarnings("UnusedParameters") int version) {
-        super(context, DOMAINS_DATABASE, cursorFactory, SCHEMA_VERSION);
-
-        // Store a handle for the context.
-        appContext = context;
-    }
-
-    @Override
-    public void onCreate(SQLiteDatabase domainsDatabase) {
-        // Create the domains table.
-        domainsDatabase.execSQL(CREATE_DOMAINS_TABLE);
-    }
-
-    @Override
-    public void onUpgrade(SQLiteDatabase domainsDatabase, int oldVersion, int newVersion) {
-        // Upgrade the database table.
-        switch (oldVersion) {
-            // Upgrade from schema version 1.
-            case 1:
-                // Add the display images column.
-                domainsDatabase.execSQL("ALTER TABLE " + DOMAINS_TABLE + " ADD COLUMN " + DISPLAY_IMAGES + " INTEGER");
-
-            // Upgrade from schema version 2.
-            case 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 3.
-            case 3:
-                // Add the night mode column.
-                domainsDatabase.execSQL("ALTER TABLE " + DOMAINS_TABLE + " ADD COLUMN nightmode INTEGER");
-
-            // Upgrade from schema version 4.
-            case 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.
-                SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(appContext);
-
-                // Get the default block list settings.
-                boolean easyListEnabled = sharedPreferences.getBoolean("easylist", true);
-                boolean easyPrivacyEnabled = sharedPreferences.getBoolean("easyprivacy", true);
-                boolean fanboyAnnoyanceListEnabled = sharedPreferences.getBoolean("fanboys_annoyance_list", true);
-                boolean 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);
-                }
-
-            // Upgrade from schema version 5.
-            case 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");
-
-            // Upgrade from schema version 6.
-            case 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");
-
-            // Upgrade from schema version 7.
-            case 7:
-                // Add the UltraPrivacy column.
-                domainsDatabase.execSQL("ALTER TABLE " + DOMAINS_TABLE + " ADD COLUMN " + ENABLE_ULTRAPRIVACY + " BOOLEAN");
-
-                // Enable it for all existing rows.
-                domainsDatabase.execSQL("UPDATE " + DOMAINS_TABLE + " SET " + ENABLE_ULTRAPRIVACY + " = " + 1);
-
-            // Upgrade from schema version 8.
-            case 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 9.
-            case 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");
-
-            // Upgrade from schema version 10.
-            case 10:
-                // Add the UltraList column.
-                domainsDatabase.execSQL("ALTER TABLE " + DOMAINS_TABLE + " ADD COLUMN " + ULTRALIST + " BOOLEAN");
-
-                // Enable it for all existing rows.
-                domainsDatabase.execSQL("UPDATE " + DOMAINS_TABLE + " SET " + ULTRALIST + " = " + 1);
-
-            // Upgrade from schema version 11.
-            case 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");
-
-                // SQLite amazingly doesn't include an easy command to delete a column.  <https://www.sqlite.org/lang_altertable.html>
-                // 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.
-            case 12:
-                // 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");
-        }
-    }
-
-    Cursor getCompleteCursorOrderedByDomain() {
-        // Get a readable database handle.
-        SQLiteDatabase domainsDatabase = this.getReadableDatabase();
-
-        // Return everything in the domains table ordered by the domain name.  The second argument is `null` because there are no `selectionArgs`.
-        return domainsDatabase.rawQuery("SELECT * FROM " + DOMAINS_TABLE + " ORDER BY " + DOMAIN_NAME + " ASC", null);
-    }
-
-    public Cursor getDomainNameCursorOrderedByDomain() {
-        // Get a readable database handle.
-        SQLiteDatabase domainsDatabase = this.getReadableDatabase();
-
-        // Get everything in the domains table ordered by the domain name.
-        String GET_CURSOR_ORDERED_BY_DOMAIN = "SELECT " + _ID + ", " + DOMAIN_NAME +
-                " FROM " + DOMAINS_TABLE +
-                " ORDER BY " + DOMAIN_NAME + " ASC";
-
-        // Return the results as a `Cursor`.  The second argument is `null` because there are no `selectionArgs`.  The cursor can't be closed because it is needed in the calling activity.
-        return domainsDatabase.rawQuery(GET_CURSOR_ORDERED_BY_DOMAIN, null);
-    }
-
-    public Cursor getDomainNameCursorOrderedByDomainExcept(int databaseId) {
-        // Get a readable database handle.
-        SQLiteDatabase domainsDatabase = this.getReadableDatabase();
-
-        // Prepare the SQL statement to select all rows except that with `databaseId`.
-        String GET_CURSOR_ORDERED_BY_DOMAIN_EXCEPT = "SELECT " + _ID + ", " + DOMAIN_NAME +
-                " FROM " + DOMAINS_TABLE +
-                " WHERE " + _ID + " IS NOT " + databaseId +
-                " ORDER BY " + DOMAIN_NAME + " ASC";
-
-        // Return the results as a `Cursor`.  The second argument is `null` because there are no `selectionArgs`.  The cursor can't be closed because it is needed in the calling activity.
-        return domainsDatabase.rawQuery(GET_CURSOR_ORDERED_BY_DOMAIN_EXCEPT, null);
-    }
-
-    public Cursor getCursorForId(int databaseId) {
-        // Get a readable database handle.
-        SQLiteDatabase domainsDatabase = this.getReadableDatabase();
-
-        // Prepare the SQL statement to get the `Cursor` for `databaseId`.
-        String GET_CURSOR_FOR_ID = "SELECT * FROM " + DOMAINS_TABLE +
-                " WHERE " + _ID + " = " + databaseId;
-
-        // Return the results as a `Cursor`.  The second argument is `null` because there are no `selectionArgs`.  The cursor can't be closed because it is needed in the calling activity.
-        return domainsDatabase.rawQuery(GET_CURSOR_FOR_ID, null);
-    }
-
-    public Cursor getCursorForDomainName(String domainName) {
-        // Get a readable database handle.
-        SQLiteDatabase domainsDatabase = this.getReadableDatabase();
-
-        // Return a cursor for the requested domain name.
-        return domainsDatabase.query(DOMAINS_TABLE, null, DOMAIN_NAME + " = " + "\"" + domainName + "\"", null, null, null, null);
-
-    }
-
-    public int addDomain(String domainName) {
-        // Store the domain data in a `ContentValues`.
-        ContentValues domainContentValues = new ContentValues();
-
-        // Get a handle for the shared preference.
-        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(appContext);
-
-        // Get the default settings.
-        boolean javaScript = sharedPreferences.getBoolean("javascript", false);
-        boolean cookies = sharedPreferences.getBoolean(appContext.getString(R.string.cookies_key), false);
-        boolean domStorage = sharedPreferences.getBoolean("dom_storage", false);
-        boolean saveFormData = sharedPreferences.getBoolean("save_form_data", false);  // Form data can be removed once the minimum API >= 26.
-        boolean easyList = sharedPreferences.getBoolean("easylist", true);
-        boolean easyPrivacy = sharedPreferences.getBoolean("easyprivacy", true);
-        boolean fanboyAnnoyanceList = sharedPreferences.getBoolean("fanboys_annoyance_list", true);
-        boolean fanboySocialBlockingList = sharedPreferences.getBoolean("fanboys_social_blocking_list", true);
-        boolean ultraList = sharedPreferences.getBoolean("ultralist", true);
-        boolean ultraPrivacy = sharedPreferences.getBoolean("ultraprivacy", true);
-        boolean blockAllThirdPartyRequests = sharedPreferences.getBoolean("block_all_third_party_requests", false);
-
-        // Create entries for the database fields.  The ID is created automatically.  The pinned SSL certificate information is not created unless added by the user.
-        domainContentValues.put(DOMAIN_NAME, domainName);
-        domainContentValues.put(ENABLE_JAVASCRIPT, javaScript);
-        domainContentValues.put(COOKIES, cookies);
-        domainContentValues.put(ENABLE_DOM_STORAGE, domStorage);
-        domainContentValues.put(ENABLE_FORM_DATA, saveFormData);  // Form data can be removed once the minimum API >= 26.
-        domainContentValues.put(ENABLE_EASYLIST, easyList);
-        domainContentValues.put(ENABLE_EASYPRIVACY, easyPrivacy);
-        domainContentValues.put(ENABLE_FANBOYS_ANNOYANCE_LIST, fanboyAnnoyanceList);
-        domainContentValues.put(ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST, fanboySocialBlockingList);
-        domainContentValues.put(ULTRALIST, ultraList);
-        domainContentValues.put(ENABLE_ULTRAPRIVACY, ultraPrivacy);
-        domainContentValues.put(BLOCK_ALL_THIRD_PARTY_REQUESTS, blockAllThirdPartyRequests);
-        domainContentValues.put(USER_AGENT, "System default user agent");
-        domainContentValues.put(FONT_SIZE, 0);
-        domainContentValues.put(SWIPE_TO_REFRESH, 0);
-        domainContentValues.put(WEBVIEW_THEME, 0);
-        domainContentValues.put(WIDE_VIEWPORT, 0);
-        domainContentValues.put(DISPLAY_IMAGES, 0);
-
-        // Get a writable database handle.
-        SQLiteDatabase domainsDatabase = this.getWritableDatabase();
-
-        // Insert a new row and store the resulting database ID.  The second argument is `null`, which makes it so that a completely null row cannot be created.
-        int newDomainDatabaseId  = (int) domainsDatabase.insert(DOMAINS_TABLE, null, domainContentValues);
-
-        // Close the database handle.
-        domainsDatabase.close();
-
-        // Return the new domain database ID.
-        return newDomainDatabaseId;
-    }
-
-    void addDomain(ContentValues contentValues) {
-        // Get a writable database handle.
-        SQLiteDatabase domainsDatabase = this.getWritableDatabase();
-
-        // Add the new domain.
-        domainsDatabase.insert(DOMAINS_TABLE, null, contentValues);
-
-        // Close the database handle.
-        domainsDatabase.close();
-    }
-
-    public void updateDomain(int databaseId, String domainName, boolean javaScript, boolean cookies, boolean domStorage, boolean formData, boolean easyList, boolean easyPrivacy, boolean fanboysAnnoyance,
-                             boolean fanboysSocialBlocking, boolean ultraList, boolean ultraPrivacy, boolean blockAllThirdPartyRequests, String userAgent, int fontSize, int swipeToRefresh, int webViewTheme,
-                             int wideViewport, int displayImages, boolean pinnedSslCertificate, boolean pinnedIpAddresses) {
-
-        // Store the domain data in a content values.
-        ContentValues domainContentValues = new ContentValues();
-
-        // Add entries for each field in the database.
-        domainContentValues.put(DOMAIN_NAME, domainName);
-        domainContentValues.put(ENABLE_JAVASCRIPT, javaScript);
-        domainContentValues.put(COOKIES, cookies);
-        domainContentValues.put(ENABLE_DOM_STORAGE, domStorage);
-        domainContentValues.put(ENABLE_FORM_DATA, formData);  // Form data can be removed once the minimum API >= 26.
-        domainContentValues.put(ENABLE_EASYLIST, easyList);
-        domainContentValues.put(ENABLE_EASYPRIVACY, easyPrivacy);
-        domainContentValues.put(ENABLE_FANBOYS_ANNOYANCE_LIST, fanboysAnnoyance);
-        domainContentValues.put(ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST, fanboysSocialBlocking);
-        domainContentValues.put(ULTRALIST, ultraList);
-        domainContentValues.put(ENABLE_ULTRAPRIVACY, ultraPrivacy);
-        domainContentValues.put(BLOCK_ALL_THIRD_PARTY_REQUESTS, blockAllThirdPartyRequests);
-        domainContentValues.put(USER_AGENT, userAgent);
-        domainContentValues.put(FONT_SIZE, fontSize);
-        domainContentValues.put(SWIPE_TO_REFRESH, swipeToRefresh);
-        domainContentValues.put(WEBVIEW_THEME, webViewTheme);
-        domainContentValues.put(WIDE_VIEWPORT, wideViewport);
-        domainContentValues.put(DISPLAY_IMAGES, displayImages);
-        domainContentValues.put(PINNED_SSL_CERTIFICATE, pinnedSslCertificate);
-        domainContentValues.put(PINNED_IP_ADDRESSES, pinnedIpAddresses);
-
-        // Get a writable database handle.
-        SQLiteDatabase domainsDatabase = this.getWritableDatabase();
-
-        // Update the row for `databaseId`.  The last argument is `null` because there are no `whereArgs`.
-        domainsDatabase.update(DOMAINS_TABLE, domainContentValues, _ID + " = " + databaseId, null);
-
-        // Close the database handle.
-        domainsDatabase.close();
-    }
-
-    public void updatePinnedSslCertificate(int databaseId, String sslIssuedToCommonName, String sslIssuedToOrganization, String sslIssuedToOrganizationalUnit, String sslIssuedByCommonName,
-                                     String sslIssuedByOrganization, String sslIssuedByOrganizationalUnit, long sslStartDate, long sslEndDate) {
-
-        // Store the pinned SSL certificate in a content values.
-        ContentValues pinnedSslCertificateContentValues = new ContentValues();
-
-        // Add entries for each field in the certificate.
-        pinnedSslCertificateContentValues.put(SSL_ISSUED_TO_COMMON_NAME, sslIssuedToCommonName);
-        pinnedSslCertificateContentValues.put(SSL_ISSUED_TO_ORGANIZATION, sslIssuedToOrganization);
-        pinnedSslCertificateContentValues.put(SSL_ISSUED_TO_ORGANIZATIONAL_UNIT, sslIssuedToOrganizationalUnit);
-        pinnedSslCertificateContentValues.put(SSL_ISSUED_BY_COMMON_NAME, sslIssuedByCommonName);
-        pinnedSslCertificateContentValues.put(SSL_ISSUED_BY_ORGANIZATION, sslIssuedByOrganization);
-        pinnedSslCertificateContentValues.put(SSL_ISSUED_BY_ORGANIZATIONAL_UNIT, sslIssuedByOrganizationalUnit);
-        pinnedSslCertificateContentValues.put(SSL_START_DATE, sslStartDate);
-        pinnedSslCertificateContentValues.put(SSL_END_DATE, sslEndDate);
-
-        // Get a writable database handle.
-        SQLiteDatabase domainsDatabase = this.getWritableDatabase();
-
-        // Update the row for database ID.
-        domainsDatabase.update(DOMAINS_TABLE, pinnedSslCertificateContentValues, _ID + " = " + databaseId, null);
-
-        // Close the database handle.
-        domainsDatabase.close();
-    }
-
-    public void updatePinnedIpAddresses(int databaseId, String ipAddresses) {
-        // Store the pinned IP addresses in a content values.
-        ContentValues pinnedIpAddressesContentValues = new ContentValues();
-
-        // Add the IP addresses to the content values.
-        pinnedIpAddressesContentValues.put(IP_ADDRESSES, ipAddresses);
-
-        // Get a writable database handle.
-        SQLiteDatabase domainsDatabase = this.getWritableDatabase();
-
-        // Update the row for the database ID.
-        domainsDatabase.update(DOMAINS_TABLE, pinnedIpAddressesContentValues, _ID + " = " + databaseId, null);
-
-        // Close the database handle.
-        domainsDatabase.close();
-    }
-
-    public void deleteDomain(int databaseId) {
-        // Get a writable database handle.
-        SQLiteDatabase domainsDatabase = this.getWritableDatabase();
-
-        // Delete the row for `databaseId`.  The last argument is `null` because we don't need additional parameters.
-        domainsDatabase.delete(DOMAINS_TABLE, _ID + " = " + databaseId, null);
-
-        // Close the database handle.
-        domainsDatabase.close();
-    }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/stoutner/privacybrowser/helpers/DomainsDatabaseHelper.kt b/app/src/main/java/com/stoutner/privacybrowser/helpers/DomainsDatabaseHelper.kt
new file mode 100644 (file)
index 0000000..4fc6a08
--- /dev/null
@@ -0,0 +1,459 @@
+/*
+ * Copyright © 2017-2022 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
+ *
+ * Privacy Browser Android is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Privacy Browser Android is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Privacy Browser Android.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.stoutner.privacybrowser.helpers
+
+import android.content.ContentValues
+import android.content.Context
+import android.database.Cursor
+import android.database.DatabaseUtils
+import android.database.sqlite.SQLiteDatabase
+import android.database.sqlite.SQLiteOpenHelper
+
+import androidx.preference.PreferenceManager
+
+import com.stoutner.privacybrowser.R
+
+// The private constants.
+private const val SCHEMA_VERSION = 13
+
+class DomainsDatabaseHelper(private val appContext: Context) : SQLiteOpenHelper(appContext, DOMAINS_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.
+        const val DOMAINS_DATABASE = "domains.db"
+        const val DOMAINS_TABLE = "domains"
+
+        // The spinner constants.
+        const val SYSTEM_DEFAULT = 0
+        const val ENABLED = 1
+        const val DISABLED = 2
+        const val LIGHT_THEME = 1
+        const val DARK_THEME = 2
+
+        // The schema constants.
+        const val ID = "_id"
+        const val DOMAIN_NAME = "domainname"
+        const val ENABLE_JAVASCRIPT = "enablejavascript"
+        const val COOKIES = "cookies"
+        const val ENABLE_DOM_STORAGE = "enabledomstorage"
+        const val ENABLE_FORM_DATA = "enableformdata" // Form data can be removed once the minimum API >= 26.
+        const val ENABLE_EASYLIST = "enableeasylist"
+        const val ENABLE_EASYPRIVACY = "enableeasyprivacy"
+        const val ENABLE_FANBOYS_ANNOYANCE_LIST = "enablefanboysannoyancelist"
+        const val ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST = "enablefanboyssocialblockinglist"
+        const val ULTRALIST = "ultralist"
+        const val ENABLE_ULTRAPRIVACY = "enableultraprivacy"
+        const val BLOCK_ALL_THIRD_PARTY_REQUESTS = "blockallthirdpartyrequests"
+        const val USER_AGENT = "useragent"
+        const val FONT_SIZE = "fontsize"
+        const val SWIPE_TO_REFRESH = "swipetorefresh"
+        const val WEBVIEW_THEME = "webview_theme"
+        const val WIDE_VIEWPORT = "wide_viewport"
+        const val DISPLAY_IMAGES = "displayimages"
+        const val PINNED_SSL_CERTIFICATE = "pinnedsslcertificate"
+        const val SSL_ISSUED_TO_COMMON_NAME = "sslissuedtocommonname"
+        const val SSL_ISSUED_TO_ORGANIZATION = "sslissuedtoorganization"
+        const val SSL_ISSUED_TO_ORGANIZATIONAL_UNIT = "sslissuedtoorganizationalunit"
+        const val SSL_ISSUED_BY_COMMON_NAME = "sslissuedbycommonname"
+        const val SSL_ISSUED_BY_ORGANIZATION = "sslissuedbyorganization"
+        const val SSL_ISSUED_BY_ORGANIZATIONAL_UNIT = "sslissuedbyorganizationalunit"
+        const val SSL_START_DATE = "sslstartdate"
+        const val SSL_END_DATE = "sslenddate"
+        const val PINNED_IP_ADDRESSES = "pinned_ip_addresses"
+        const val IP_ADDRESSES = "ip_addresses"
+
+        // The table creation constant.
+        const val CREATE_DOMAINS_TABLE = "CREATE TABLE " + DOMAINS_TABLE + " (" +
+                ID + " INTEGER PRIMARY KEY, " +
+                DOMAIN_NAME + " TEXT, " +
+                ENABLE_JAVASCRIPT + " BOOLEAN, " +
+                COOKIES + " BOOLEAN, " +
+                ENABLE_DOM_STORAGE + " BOOLEAN, " +
+                ENABLE_FORM_DATA + " BOOLEAN, " +
+                ENABLE_EASYLIST + " BOOLEAN, " +
+                ENABLE_EASYPRIVACY + " BOOLEAN, " +
+                ENABLE_FANBOYS_ANNOYANCE_LIST + " BOOLEAN, " +
+                ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST + " BOOLEAN, " +
+                ULTRALIST + " BOOLEAN, " +
+                ENABLE_ULTRAPRIVACY + " BOOLEAN, " +
+                BLOCK_ALL_THIRD_PARTY_REQUESTS + " BOOLEAN, " +
+                USER_AGENT + " TEXT, " +
+                FONT_SIZE + " INTEGER, " +
+                SWIPE_TO_REFRESH + " INTEGER, " +
+                WEBVIEW_THEME + " INTEGER, " +
+                WIDE_VIEWPORT + " INTEGER, " +
+                DISPLAY_IMAGES + " INTEGER, " +
+                PINNED_SSL_CERTIFICATE + " BOOLEAN, " +
+                SSL_ISSUED_TO_COMMON_NAME + " TEXT, " +
+                SSL_ISSUED_TO_ORGANIZATION + " TEXT, " +
+                SSL_ISSUED_TO_ORGANIZATIONAL_UNIT + " TEXT, " +
+                SSL_ISSUED_BY_COMMON_NAME + " TEXT, " +
+                SSL_ISSUED_BY_ORGANIZATION + " TEXT, " +
+                SSL_ISSUED_BY_ORGANIZATIONAL_UNIT + " TEXT, " +
+                SSL_START_DATE + " INTEGER, " +
+                SSL_END_DATE + " INTEGER, " +
+                PINNED_IP_ADDRESSES + " BOOLEAN, " +
+                IP_ADDRESSES + " TEXT)"
+    }
+
+    override fun onCreate(domainsDatabase: SQLiteDatabase) {
+        // Create the domains table.
+        domainsDatabase.execSQL(CREATE_DOMAINS_TABLE)
+    }
+
+    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 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 3.
+            3 -> {
+                // Add the night mode column.
+                domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN nightmode INTEGER")
+            }
+
+            // 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")
+                }
+            }
+
+            // 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")
+            }
+
+            // 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")
+            }
+
+            // Upgrade from schema version 7.
+            7 -> {
+                // Add the UltraPrivacy column.
+                domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $ENABLE_ULTRAPRIVACY BOOLEAN")
+
+                // Enable it for all existing rows.
+                domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ENABLE_ULTRAPRIVACY = 1")
+            }
+
+            // 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 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")
+            }
+
+            // Upgrade from schema version 10.
+            10 -> {
+                // Add the UltraList column.
+                domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $ULTRALIST BOOLEAN")
+
+                // Enable it for all existing rows.
+                domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ULTRALIST = 1")
+            }
+
+            // 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")
+
+                // SQLite amazingly only added a command to drop a column in version 3.35.0.  <https://www.sqlite.org/changes.html>
+                // It will be a while before that is supported in Android.  <https://developer.android.com/reference/android/database/sqlite/package-summary>
+                // Although a new table could be created and all the data copied to it, I think I will just leave the old night mode column.  It will be wiped out the next time an import is run.
+            }
+
+            // Upgrade from schema version 12.
+            12 -> {
+                // 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")
+            }
+        }
+    }
+
+    val completeCursorOrderedByDomain: Cursor
+        get() {
+            // Get a readable database handle.
+            val domainsDatabase = this.readableDatabase
+
+            // 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.
+            return domainsDatabase.rawQuery("SELECT * FROM $DOMAINS_TABLE ORDER BY $DOMAIN_NAME ASC", null)
+        }
+
+    val domainNameCursorOrderedByDomain: Cursor
+        get() {
+            // Get a readable database handle.
+            val domainsDatabase = this.readableDatabase
+
+            // 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.
+            return domainsDatabase.rawQuery("SELECT $ID, $DOMAIN_NAME FROM $DOMAINS_TABLE ORDER BY $DOMAIN_NAME ASC", null)
+        }
+
+    fun getDomainNameCursorOrderedByDomainExcept(databaseId: Int): Cursor {
+        // Get a readable database handle.
+        val domainsDatabase = this.readableDatabase
+
+        // 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.
+        return domainsDatabase.rawQuery("SELECT $ID, $DOMAIN_NAME FROM $DOMAINS_TABLE WHERE $ID IS NOT $databaseId ORDER BY $DOMAIN_NAME ASC", null)
+    }
+
+    fun getCursorForId(databaseId: Int): Cursor {
+        // Get a readable database handle.
+        val domainsDatabase = this.readableDatabase
+
+        // Return a cursor for the specified database ID.  The cursor can't be closed because it is needed in the calling activity.
+        return domainsDatabase.rawQuery("SELECT * FROM $DOMAINS_TABLE WHERE $ID = $databaseId", null)
+    }
+
+    fun getCursorForDomainName(domainName: String): Cursor {
+        // Get a readable database handle.
+        val domainsDatabase = this.readableDatabase
+
+        // SQL escape the domain name.
+        val sqlEscapedDomainName = DatabaseUtils.sqlEscapeString(domainName)
+
+        // Return a cursor for the requested domain name.  The cursor can't be closed because it is needed in the calling activity.
+        return domainsDatabase.rawQuery("SELECT * FROM $DOMAINS_TABLE WHERE $DOMAIN_NAME = $sqlEscapedDomainName", null)
+    }
+
+    fun addDomain(domainName: String): Int {
+        // Instantiate a content values.
+        val domainContentValues = ContentValues()
+
+        // Get a handle for the shared preference.
+        val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(appContext)
+
+        // Get the default settings.
+        val javaScript = sharedPreferences.getBoolean("javascript", false)
+        val cookies = sharedPreferences.getBoolean(appContext.getString(R.string.cookies_key), false)
+        val domStorage = sharedPreferences.getBoolean("dom_storage", false)
+        val saveFormData = sharedPreferences.getBoolean("save_form_data", false) // Form data can be removed once the minimum API >= 26.
+        val easyList = sharedPreferences.getBoolean("easylist", true)
+        val easyPrivacy = sharedPreferences.getBoolean("easyprivacy", true)
+        val fanboyAnnoyanceList = sharedPreferences.getBoolean("fanboys_annoyance_list", true)
+        val fanboySocialBlockingList = sharedPreferences.getBoolean("fanboys_social_blocking_list", true)
+        val ultraList = sharedPreferences.getBoolean("ultralist", true)
+        val ultraPrivacy = sharedPreferences.getBoolean("ultraprivacy", true)
+        val blockAllThirdPartyRequests = sharedPreferences.getBoolean("block_all_third_party_requests", false)
+
+        // Create entries for the database fields.  The ID is created automatically.  The pinned SSL certificate information is not created unless added by the user.
+        domainContentValues.put(DOMAIN_NAME, domainName)
+        domainContentValues.put(ENABLE_JAVASCRIPT, javaScript)
+        domainContentValues.put(COOKIES, cookies)
+        domainContentValues.put(ENABLE_DOM_STORAGE, domStorage)
+        domainContentValues.put(ENABLE_FORM_DATA, saveFormData) // Form data can be removed once the minimum API >= 26.
+        domainContentValues.put(ENABLE_EASYLIST, easyList)
+        domainContentValues.put(ENABLE_EASYPRIVACY, easyPrivacy)
+        domainContentValues.put(ENABLE_FANBOYS_ANNOYANCE_LIST, fanboyAnnoyanceList)
+        domainContentValues.put(ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST, fanboySocialBlockingList)
+        domainContentValues.put(ULTRALIST, ultraList)
+        domainContentValues.put(ENABLE_ULTRAPRIVACY, ultraPrivacy)
+        domainContentValues.put(BLOCK_ALL_THIRD_PARTY_REQUESTS, blockAllThirdPartyRequests)
+        domainContentValues.put(USER_AGENT, "System default user agent")
+        domainContentValues.put(FONT_SIZE, 0)
+        domainContentValues.put(SWIPE_TO_REFRESH, 0)
+        domainContentValues.put(WEBVIEW_THEME, 0)
+        domainContentValues.put(WIDE_VIEWPORT, 0)
+        domainContentValues.put(DISPLAY_IMAGES, 0)
+
+        // Get a writable database handle.
+        val domainsDatabase = this.writableDatabase
+
+        // Insert a new row and store the resulting database ID.
+        val newDomainDatabaseId = domainsDatabase.insert(DOMAINS_TABLE, null, domainContentValues).toInt()
+
+        // Close the database handle.
+        domainsDatabase.close()
+
+        // Return the new domain database ID.
+        return newDomainDatabaseId
+    }
+
+    fun addDomain(contentValues: ContentValues) {
+        // Get a writable database handle.
+        val domainsDatabase = this.writableDatabase
+
+        // Add the new domain.
+        domainsDatabase.insert(DOMAINS_TABLE, null, contentValues)
+
+        // Close the database handle.
+        domainsDatabase.close()
+    }
+
+    fun updateDomain(databaseId: Int, domainName: String, javaScript: Boolean, cookies: Boolean, domStorage: Boolean, formData: Boolean, easyList: Boolean, easyPrivacy: Boolean, fanboysAnnoyance: Boolean,
+                     fanboysSocialBlocking: Boolean, ultraList: Boolean, ultraPrivacy: Boolean, blockAllThirdPartyRequests: Boolean, userAgent: String, fontSize: Int, swipeToRefresh: Int, webViewTheme: Int,
+                     wideViewport: Int, displayImages: Int, pinnedSslCertificate: Boolean, pinnedIpAddresses: Boolean) {
+        // Instantiate a content values.
+        val domainContentValues = ContentValues()
+
+        // Add entries for each field in the database.
+        domainContentValues.put(DOMAIN_NAME, domainName)
+        domainContentValues.put(ENABLE_JAVASCRIPT, javaScript)
+        domainContentValues.put(COOKIES, cookies)
+        domainContentValues.put(ENABLE_DOM_STORAGE, domStorage)
+        domainContentValues.put(ENABLE_FORM_DATA, formData) // Form data can be removed once the minimum API >= 26.
+        domainContentValues.put(ENABLE_EASYLIST, easyList)
+        domainContentValues.put(ENABLE_EASYPRIVACY, easyPrivacy)
+        domainContentValues.put(ENABLE_FANBOYS_ANNOYANCE_LIST, fanboysAnnoyance)
+        domainContentValues.put(ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST, fanboysSocialBlocking)
+        domainContentValues.put(ULTRALIST, ultraList)
+        domainContentValues.put(ENABLE_ULTRAPRIVACY, ultraPrivacy)
+        domainContentValues.put(BLOCK_ALL_THIRD_PARTY_REQUESTS, blockAllThirdPartyRequests)
+        domainContentValues.put(USER_AGENT, userAgent)
+        domainContentValues.put(FONT_SIZE, fontSize)
+        domainContentValues.put(SWIPE_TO_REFRESH, swipeToRefresh)
+        domainContentValues.put(WEBVIEW_THEME, webViewTheme)
+        domainContentValues.put(WIDE_VIEWPORT, wideViewport)
+        domainContentValues.put(DISPLAY_IMAGES, displayImages)
+        domainContentValues.put(PINNED_SSL_CERTIFICATE, pinnedSslCertificate)
+        domainContentValues.put(PINNED_IP_ADDRESSES, pinnedIpAddresses)
+
+        // Get a writable database handle.
+        val domainsDatabase = this.writableDatabase
+
+        // Update the row for the specified database ID.
+        domainsDatabase.update(DOMAINS_TABLE, domainContentValues, "$ID = $databaseId", null)
+
+        // Close the database handle.
+        domainsDatabase.close()
+    }
+
+    fun updatePinnedSslCertificate(databaseId: Int, sslIssuedToCommonName: String, sslIssuedToOrganization: String, sslIssuedToOrganizationalUnit: String, sslIssuedByCommonName: String,
+                                   sslIssuedByOrganization: String, sslIssuedByOrganizationalUnit: String, sslStartDate: Long, sslEndDate: Long) {
+        // Instantiate a content values.
+        val pinnedSslCertificateContentValues = ContentValues()
+
+        // Add entries for each field in the certificate.
+        pinnedSslCertificateContentValues.put(SSL_ISSUED_TO_COMMON_NAME, sslIssuedToCommonName)
+        pinnedSslCertificateContentValues.put(SSL_ISSUED_TO_ORGANIZATION, sslIssuedToOrganization)
+        pinnedSslCertificateContentValues.put(SSL_ISSUED_TO_ORGANIZATIONAL_UNIT, sslIssuedToOrganizationalUnit)
+        pinnedSslCertificateContentValues.put(SSL_ISSUED_BY_COMMON_NAME, sslIssuedByCommonName)
+        pinnedSslCertificateContentValues.put(SSL_ISSUED_BY_ORGANIZATION, sslIssuedByOrganization)
+        pinnedSslCertificateContentValues.put(SSL_ISSUED_BY_ORGANIZATIONAL_UNIT, sslIssuedByOrganizationalUnit)
+        pinnedSslCertificateContentValues.put(SSL_START_DATE, sslStartDate)
+        pinnedSslCertificateContentValues.put(SSL_END_DATE, sslEndDate)
+
+        // Get a writable database handle.
+        val domainsDatabase = this.writableDatabase
+
+        // Update the row for the specified database ID.
+        domainsDatabase.update(DOMAINS_TABLE, pinnedSslCertificateContentValues, "$ID = $databaseId", null)
+
+        // Close the database handle.
+        domainsDatabase.close()
+    }
+
+    fun updatePinnedIpAddresses(databaseId: Int, ipAddresses: String) {
+        // Instantiate a content values.
+        val pinnedIpAddressesContentValues = ContentValues()
+
+        // Add the IP addresses to the content values.
+        pinnedIpAddressesContentValues.put(IP_ADDRESSES, ipAddresses)
+
+        // Get a writable database handle.
+        val domainsDatabase = this.writableDatabase
+
+        // Update the row for the database ID.
+        domainsDatabase.update(DOMAINS_TABLE, pinnedIpAddressesContentValues, "$ID = $databaseId", null)
+
+        // Close the database handle.
+        domainsDatabase.close()
+    }
+
+    fun deleteDomain(databaseId: Int) {
+        // Get a writable database handle.
+        val domainsDatabase = this.writableDatabase
+
+        // Delete the row for the specified database ID.
+        domainsDatabase.delete(DOMAINS_TABLE, "$ID = $databaseId", null)
+
+        // Close the database handle.
+        domainsDatabase.close()
+    }
+}
\ No newline at end of file
index 747123f8efc70bd495054146776bbeadeb152d0c..096364f643458b70baff0961d1790943f36e5671 100644 (file)
@@ -377,8 +377,8 @@ public class ImportExportDatabaseHelper {
             // Delete the current bookmarks database.
             context.deleteDatabase(BookmarksDatabaseHelper.BOOKMARKS_DATABASE);
 
-            // Create a new bookmarks database.  The `0` specifies the database version, but that is ignored and set instead using a constant in `BookmarksDatabaseHelper`.
-            BookmarksDatabaseHelper bookmarksDatabaseHelper = new BookmarksDatabaseHelper(context, null, null, 0);
+            // Create a new bookmarks database.
+            BookmarksDatabaseHelper bookmarksDatabaseHelper = new BookmarksDatabaseHelper(context);
 
             // Move to the first bookmark.
             importBookmarksCursor.moveToFirst();
@@ -414,8 +414,8 @@ public class ImportExportDatabaseHelper {
             // Delete the current domains database.
             context.deleteDatabase(DomainsDatabaseHelper.DOMAINS_DATABASE);
 
-            // Create a new domains database.  The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`.
-            DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(context, null, null, 0);
+            // Create a new domains database.
+            DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(context);
 
             // Move to the first domain.
             importDomainsCursor.moveToFirst();
@@ -563,8 +563,8 @@ public class ImportExportDatabaseHelper {
             // Create the temporary export database bookmarks table.
             temporaryExportDatabase.execSQL(BookmarksDatabaseHelper.CREATE_BOOKMARKS_TABLE);
 
-            // Open the bookmarks database.  The `0` specifies the database version, but that is ignored and set instead using a constant in `BookmarksDatabaseHelper`.
-            BookmarksDatabaseHelper bookmarksDatabaseHelper = new BookmarksDatabaseHelper(context, null, null, 0);
+            // Open the bookmarks database.
+            BookmarksDatabaseHelper bookmarksDatabaseHelper = new BookmarksDatabaseHelper(context);
 
             // Get a full bookmarks cursor.
             Cursor bookmarksCursor = bookmarksDatabaseHelper.getAllBookmarks();
@@ -598,8 +598,8 @@ public class ImportExportDatabaseHelper {
             // Create the temporary export database domains table.
             temporaryExportDatabase.execSQL(DomainsDatabaseHelper.CREATE_DOMAINS_TABLE);
 
-            // Open the domains database.  The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`.
-            DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(context, null, null, 0);
+            // Open the domains database.
+            DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(context);
 
             // Get a full domains database cursor.
             Cursor domainsCursor = domainsDatabaseHelper.getCompleteCursorOrderedByDomain();
index 1eefe7378006ea496de2bdba521b0da9b26dc303..9edd3bf2eda1c5252de7586b83937e294cacb0e6 100644 (file)
@@ -208,22 +208,15 @@ class NestedScrollWebView @JvmOverloads constructor(context: Context, attributeS
         hasPinnedSslCertificate = true
     }
 
-    fun getPinnedSslCertificate(): ArrayList<Any> {
-        // Initialize an array list.
-        val arrayList = ArrayList<Any>()
-
+    fun getPinnedSslCertificate(): Pair<Array<String>, Array<Date>> {
         // Create the SSL certificate string array.
         val sslCertificateStringArray = arrayOf(pinnedSslIssuedToCName, pinnedSslIssuedToOName, pinnedSslIssuedToUName, pinnedSslIssuedByCName, pinnedSslIssuedByOName, pinnedSslIssuedByUName)
 
         // Create the SSL certificate date array.
         val sslCertificateDateArray = arrayOf(pinnedSslStartDate, pinnedSslEndDate)
 
-        // Add the arrays to the array list.
-        arrayList.add(sslCertificateStringArray)
-        arrayList.add(sslCertificateDateArray)
-
-        // Return the pinned SSL certificate array list.
-        return arrayList
+        // Return the pinned SSL certificate pair.
+        return Pair(sslCertificateStringArray, sslCertificateDateArray)
     }
 
     fun clearPinnedSslCertificate() {
index 027be9527a058db8fd883c8d721732033b4ece19..d07e87fb443df6651bca9b0023ed8231a9311c20 100644 (file)
     <color name="domain_settings_url_background">@color/dark_blue_40</color>
     <color name="red_text">@color/red_900</color>
     <color name="selected_icon_background">@color/gray_700</color>
-
-    <!-- Raw colors. -->
-    <color name="semi_transparent_black">#66000000</color>
-
-    <color name="black">#FF000000</color>
-    <color name="black_translucent_11">#11000000</color>
-    <color name="black_translucent_22">#22000000</color>
-    <color name="black_translucent_33">#33000000</color>
-
-    <color name="blue_50">#FFE3F2FD</color>
-    <color name="blue_100">#FFBBDEFB</color>
-    <color name="blue_200">#FF90CAF9</color>
-    <color name="blue_300">#FF64B5F6</color>
-    <color name="blue_400">#FF42A5F5</color>
-    <color name="blue_500">#FF2196F3</color>
-    <color name="blue_600">#FF1E88E5</color>
-    <color name="blue_700">#FF1976D2</color>
-    <color name="blue_700_50">#881976D2</color>
-    <color name="blue_750">#FF1770C6</color>
-    <color name="blue_800">#FF1565C0</color>
-    <color name="blue_830">#FF1760B5</color>
-    <color name="blue_900">#FF0D47A1</color>
-    <color name="blue_1000">#FF082B61</color>
-    <color name="blue_a700">#FF2962FF</color>
-
-    <color name="violet_300">#FFBAD3FB</color>
-    <color name="violet_500">#FF8AB4F8</color>
-    <color name="violet_700">#FF5785C5</color>
-    <color name="violet_a900">#FF586479</color>
-
-    <color name="blue_gray_500">#FF607D8B</color>
-
-    <color name="dark_blue_65">#FF1766A6</color>
-    <color name="dark_blue_55">#FF14568C</color>
-    <color name="dark_blue_40">#FF0E3F66</color>
-    <color name="dark_blue_30">#FF002A4D</color>
-    <color name="dark_blue_15">#FF001C33</color>
-
-    <color name="dark_green_15">#FF163317</color>
-
-    <color name="gray_50">#FFFAFAFA</color>
-    <color name="gray_100">#FFF5F5F5</color>
-    <color name="gray_200">#FFEEEEEE</color>
-    <color name="gray_300">#FFE0E0E0</color>
-    <color name="gray_350">#FFC1C1C1</color>
-    <color name="gray_400">#FFBDBDBD</color>
-    <color name="gray_425">#FFB7B7B7</color>
-    <color name="gray_500">#FF9E9E9E</color>
-    <color name="gray_600">#FF757575</color>
-    <color name="gray_700">#FF616161</color>
-    <color name="gray_750">#FF515151</color>
-    <color name="gray_800">#FF424242</color>
-    <color name="gray_850">#FF313131</color>
-    <color name="gray_860">#FF303030</color>
-    <color name="gray_875">#FF2D2D2D</color>
-    <color name="gray_900">#FF212121</color>
-    <color name="gray_925">#FF202020</color>
-
-    <color name="green_50">#FFE8F5E9</color>
-    <color name="green_100">#FFC8E6C9</color>
-    <color name="green_200">#FFA5D6A7</color>
-
-    <color name="light_green_100">#FFDCEDC8</color>
-    <color name="light_green_a700">#FF64DD17</color>
-
-    <color name="red_100">#FFFFCDD2</color>
-    <color name="red_200">#FFEF9A9A</color>
-    <color name="red_600">#FFE53935</color>
-    <color name="red_700">#FFD32F2F</color>
-    <color name="red_700_40">#55D32F2F</color>
-    <color name="red_800">#FFC62828</color>
-    <color name="red_900">#FFB71C1C</color>
-    <color name="red_1000">#FFA21212</color>
-    <color name="red_1100">#FF930606</color>
-    <color name="red_a700">#FFD50000</color>
-
-    <color name="transparent">#00000000</color>
-
-    <color name="white">#FFFFFFFF</color>
-
-    <color name="yellow_100">#FFFFF9C4</color>
-    <color name="yellow_700">#FFFBC02D</color>
-    <color name="yellow_700_50">#88FBC02D</color>
-    <color name="yellow_900">#FFF57F17</color>
-    <color name="yellow_a700">#FFFFD600</color>
 </resources>
\ No newline at end of file