From: Soren Stoutner Date: Mon, 28 Mar 2022 23:44:10 +0000 (-0700) Subject: Bump the target API to 32 (Android 12L). https://redmine.stoutner.com/issues/828 X-Git-Tag: v3.11~19 X-Git-Url: https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=commitdiff_plain;h=38919c77d15eeacbee96ab337afc62b30ddc74ca Bump the target API to 32 (Android 12L). https://redmine.stoutner.com/issues/828 --- diff --git a/app/build.gradle b/app/build.gradle index 441cc71a..8469c24d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,7 +27,7 @@ android { defaultConfig { minSdk 23 - targetSdk 31 + targetSdk 32 versionCode 59 versionName "3.10.1" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a773e56f..a9747840 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,7 +1,7 @@ + + tools:ignore="DataExtractionRules,UnusedAttribute" > - - - -

3.10.2 (version code 60)

-

4 März 2022 - Mindest-API 23, Ziel-API 31

-
    -
  • Work around a scrolling bug in Android System Webview >= 99.0.4844.48.
  • -
-

3.10.1 (version code 59)

2 März 2022 - Mindest-API 23, Ziel-API 31

    diff --git a/app/src/main/assets/en/about_changelog.html b/app/src/main/assets/en/about_changelog.html index cd7d5ce0..47e6eea0 100644 --- a/app/src/main/assets/en/about_changelog.html +++ b/app/src/main/assets/en/about_changelog.html @@ -27,12 +27,6 @@ -

    3.10.2 (version code 60)

    -

    4 March 2022 - minimum API 23, target API 31

    -
      -
    • Work around a scrolling bug in Android System Webview >= 99.0.4844.48.
    • -
    -

    3.10.1 (version code 59)

    2 March 2022 - minimum API 23, target API 31

      diff --git a/app/src/main/assets/es/about_changelog.html b/app/src/main/assets/es/about_changelog.html index 5a9cc64c..5d1c3516 100644 --- a/app/src/main/assets/es/about_changelog.html +++ b/app/src/main/assets/es/about_changelog.html @@ -29,12 +29,6 @@ -

      3.10.2 (código de versión 60)

      -

      4 de marzo de 2022 - API mínimo 23, API objetivo 31

      -
        -
      • Work around a scrolling bug in Android System Webview >= 99.0.4844.48.
      • -
      -

      3.10.1 (código de versión 59)

      2 de marzo de 2022 - API mínimo 23, API objetivo 31

        diff --git a/app/src/main/assets/fr/about_changelog.html b/app/src/main/assets/fr/about_changelog.html index d767169a..184604b3 100644 --- a/app/src/main/assets/fr/about_changelog.html +++ b/app/src/main/assets/fr/about_changelog.html @@ -29,12 +29,6 @@ -

        3.10.2 (version du code 60)

        -

        4 Mars 2022 - API minimale : 23, API optimale : 31

        -
          -
        • Work around a scrolling bug in Android System Webview >= 99.0.4844.48.
        • -
        -

        3.10.1 (version du code 59)

        2 Mars 2022 - API minimale : 23, API optimale : 31

          diff --git a/app/src/main/assets/it/about_changelog.html b/app/src/main/assets/it/about_changelog.html index da8831b0..23451a1a 100644 --- a/app/src/main/assets/it/about_changelog.html +++ b/app/src/main/assets/it/about_changelog.html @@ -29,12 +29,6 @@ -

          3.10.2 (versione codice 60)

          -

          4 Marzo 2022 - minima API 23, target API 31

          -
            -
          • Work around a scrolling bug in Android System Webview >= 99.0.4844.48.
          • -
          -

          3.10.1 (versione codice 59)

          2 Marzo 2022 - minima API 23, target API 31

            diff --git a/app/src/main/assets/pt-rBR/about_changelog.html b/app/src/main/assets/pt-rBR/about_changelog.html index b869a264..6c630143 100644 --- a/app/src/main/assets/pt-rBR/about_changelog.html +++ b/app/src/main/assets/pt-rBR/about_changelog.html @@ -29,12 +29,6 @@ -

            3.10.2 (código da versão 60)

            -

            4 March 2022 - API mínimo 23, API alvo 31

            -
              -
            • Work around a scrolling bug in Android System Webview >= 99.0.4844.48.
            • -
            -

            3.10.1 (código da versão 59)

            2 March 2022 - API mínimo 23, API alvo 31

              diff --git a/app/src/main/assets/pt-rBR/about_permissions.html b/app/src/main/assets/pt-rBR/about_permissions.html index 948c940d..1e024ced 100644 --- a/app/src/main/assets/pt-rBR/about_permissions.html +++ b/app/src/main/assets/pt-rBR/about_permissions.html @@ -29,12 +29,12 @@ -

              Have full network access

              +

              Tenha acesso total à rede

              android.permission.INTERNET

              -

              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”.

              +

              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”.

              -

              Install shortcuts

              +

              Instalar atalhos

              com.android.launcher.permission.INSTALL_SHORTCUT

              -

              Required to add shortcuts for websites to the launcher desktop.

              +

              Necessário para adicionar atalhos para sites na área de trabalho inicial.

              \ No newline at end of file diff --git a/app/src/main/assets/pt-rBR/about_privacy_policy.html b/app/src/main/assets/pt-rBR/about_privacy_policy.html index 9eab3cd4..53b46b8f 100644 --- a/app/src/main/assets/pt-rBR/about_privacy_policy.html +++ b/app/src/main/assets/pt-rBR/about_privacy_policy.html @@ -30,66 +30,66 @@

              Privacy Browser

              -

              Privacy Browser does not collect any user information.

              +

              Privacy Browser não coleta nenhuma informação do usuário.

              Google Play

              -

              Google Play has its own privacy policy. - Google provides anonymized summary installation information to developers, including the number of installs organized by the following categories.

              +

              Google Play tem sua própria política de privacidade. + Google fornece informações de instalação resumidas anônimas aos desenvolvedores, incluindo o número de instalações organizadas pelas categorias a seguir.

                -
              • Android version (eg. Android 7.1)
              • -
              • Device (eg. Samsung Galaxy S6 [zeroflte])
              • -
              • Tablets (eg. Tablets 10" and above)
              • -
              • Country (eg. United States)
              • -
              • Language (eg. English [United States])
              • -
              • App version (eg. 14)
              • -
              • Carrier (eg. T-Mobile - US)
              • +
              • Versão do Android (eg. Android 7.1)
              • +
              • Dispositivo (eg. Samsung Galaxy S6 [zeroflte])
              • +
              • Tablets (eg. Tablets 10" e superior)
              • +
              • País (eg. Estados Unidos)
              • +
              • Idioma (eg. Inglês [Estados Unidos])
              • +
              • Versão do aplicativo (eg. 14)
              • +
              • Operadora (eg. T-Mobile - US)
              -

              Google Play Ratings

              -

              Google Play has its own privacy policy. - Google provides developers with anonymized summaries of the following information related to user ratings.

              +

              Classificações do Google Play

              +

              Google Play tem sua própria política de privacidade. + Google fornece aos desenvolvedores resumos anônimos das seguintes informações relacionadas às classificações dos usuários.

                -
              • Country (eg. United States)
              • -
              • Language (eg. English)
              • -
              • App version (eg. 14)
              • -
              • Android version (eg. Android 7.1)
              • -
              • Device (eg. Google Nexus 5X [bullhead])
              • -
              • Tablets (eg. Tablets 10" and above)
              • +
              • País (eg. Estados Unidos)
              • +
              • Idioma (eg. Inglês)
              • +
              • Versão do Aplicativo (eg. 14)
              • +
              • Versão do Android (eg. Android 7.1)
              • +
              • Dispositivo (eg. Google Nexus 5X [bullhead])
              • +
              • Tablets (eg. Tablets 10" e superior)
              -

              Google Play Reviews

              -

              Google Play has its own privacy policy. - 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.

              +

              Avaliações do Google Play

              +

              Google Play tem sua própria política de privacidade. + 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.

                -
              • Version code (eg. 7)
              • -
              • Version name (eg. 1.6)
              • -
              • Android version (eg. Android 5.1)
              • -
              • Device (eg. Galaxy S6 Edge+ [zenlte])
              • -
              • Manufacturer (eg. Samsung)
              • -
              • Device type (eg. Phone)
              • -
              • CPU make (eg. Samsung)
              • -
              • CPU model (eg. Exynos 7420)
              • -
              • Screen density (eg. 560 dpi)
              • -
              • Screen size (eg. 2560 x 1440)
              • +
              • Código da versão (eg. 7)
              • +
              • Nome da versão (eg. 1.6)
              • +
              • Versão do Android (eg. Android 5.1)
              • +
              • Dispositivo (eg. Galaxy S6 Edge+ [zenlte])
              • +
              • Fabricante (eg. Samsung)
              • +
              • Tipo de dispositivo (eg. Phone)
              • +
              • Fabricante da CPU (eg. Samsung)
              • +
              • Modelo da CPU (eg. Exynos 7420)
              • +
              • Densidade da tela (eg. 560 dpi)
              • +
              • Tamanho da tela (eg. 2560 x 1440)
              • RAM (eg. 4096 MB)
              • -
              • Native platform (eg. armeabi-v7a,armeabi,arm64v8a)
              • -
              • OpenGL ES version (eg. 3.1)
              • -
              • Device language (eg. English)
              • +
              • Plataforma nativa (eg. armeabi-v7a,armeabi,arm64v8a)
              • +
              • Versão OpenGL ES (eg. 3.1)
              • +
              • Idioma do dispositivo (eg. Inglês)
              -

              Direct Communications

              -

              Users may choose to send direct communications to Stoutner, like email messages and comments on stoutner.com.

              +

              Comunicações Diretas

              +

              Os usuários podem optar por enviar comunicações diretas à Stoutner, como mensagens de e-mail e comentários em stoutner.com.

              -

              Use of Information

              -

              Stoutner may use this information to assist in the development of Privacy Browser and communicate the status of the project to users. - Stoutner will never sell this information nor transfer it to any third party that would use it for advertising or marketing.

              +

              Uso de informações

              +

              Stoutner pode usar essas informações para auxiliar no desenvolvimento do Privacy Browser e comunicar o status do projeto aos usuários. + A Stoutner nunca venderá essas informações nem as transferirá para terceiros que as usariam para publicidade ou marketing.


              -

              Revision 1.7, 14 May 2019

              +

              Revisão 1.7, 14 de Maio de 2019

              \ No newline at end of file diff --git a/app/src/main/assets/ru/about_changelog.html b/app/src/main/assets/ru/about_changelog.html index 51f8ecc5..e7575a85 100644 --- a/app/src/main/assets/ru/about_changelog.html +++ b/app/src/main/assets/ru/about_changelog.html @@ -27,12 +27,6 @@ -

              3.10.2 (код версии 60)

              -

              4 мая 2022 года - минимальный API 23, целевой API 31

              -
                -
              • Work around a scrolling bug in Android System Webview >= 99.0.4844.48.
              • -
              -

              3.10.1 (код версии 59)

              2 мая 2022 года - минимальный API 23, целевой API 31

                diff --git a/app/src/main/assets/tr/about_changelog.html b/app/src/main/assets/tr/about_changelog.html index 53f21dc4..dbc2d5df 100644 --- a/app/src/main/assets/tr/about_changelog.html +++ b/app/src/main/assets/tr/about_changelog.html @@ -27,12 +27,6 @@ -

                3.10.2 (version code 60)

                -

                4 Mart 2022 - minimum API 23, target API 31

                -
                  -
                • Work around a scrolling bug in Android System Webview >= 99.0.4844.48.
                • -
                -

                3.10.1 (version code 59)

                2 Mart 2022 - minimum API 23, target API 31

                  diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksActivity.java index cea440a4..ef2bb4ed 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksActivity.java @@ -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)) { diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksDatabaseViewActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksDatabaseViewActivity.java index 91afa6d6..dbfda387 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksDatabaseViewActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksDatabaseViewActivity.java @@ -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(); diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/DomainsActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/DomainsActivity.java index 2ef534f1..b9a4fb7d 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/DomainsActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/DomainsActivity.java @@ -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(); diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java index 3d27a4cf..ecab353f 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java @@ -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 . + 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 pinnedSslCertificateArrayList = nestedScrollWebView.getPinnedSslCertificate(); + Pair 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]) && diff --git a/app/src/main/java/com/stoutner/privacybrowser/adapters/PinnedMismatchPagerAdapter.kt b/app/src/main/java/com/stoutner/privacybrowser/adapters/PinnedMismatchPagerAdapter.kt index aad20f89..8953d854 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/adapters/PinnedMismatchPagerAdapter.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/adapters/PinnedMismatchPagerAdapter.kt @@ -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. diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/AddDomainDialog.kt b/app/src/main/java/com/stoutner/privacybrowser/dialogs/AddDomainDialog.kt index c67e23b0..1eb8858a 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/AddDomainDialog.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/AddDomainDialog.kt @@ -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(R.id.domain_name_edittext)!! diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateBookmarkFolderDialog.kt b/app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateBookmarkFolderDialog.kt index 553e5b83..3327e1f4 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateBookmarkFolderDialog.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateBookmarkFolderDialog.kt @@ -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 { diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDatabaseViewDialog.kt b/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDatabaseViewDialog.kt index 4e4600de..2e5a04f4 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDatabaseViewDialog.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDatabaseViewDialog.kt @@ -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) diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDialog.kt b/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDialog.kt index e30cf5db..00c7a079 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDialog.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDialog.kt @@ -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) diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDatabaseViewDialog.kt b/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDatabaseViewDialog.kt index d7f29bf7..8ef895c4 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDatabaseViewDialog.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDatabaseViewDialog.kt @@ -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) diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDialog.kt b/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDialog.kt index 6e48cfea..a386acba 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDialog.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDialog.kt @@ -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) diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/MoveToFolderDialog.kt b/app/src/main/java/com/stoutner/privacybrowser/dialogs/MoveToFolderDialog.kt index 643e79cd..101e6b32 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/MoveToFolderDialog.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/MoveToFolderDialog.kt @@ -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) diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/PinnedMismatchDialog.kt b/app/src/main/java/com/stoutner/privacybrowser/dialogs/PinnedMismatchDialog.kt index 2d1cdd7d..56ec5192 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/PinnedMismatchDialog.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/PinnedMismatchDialog.kt @@ -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()) { diff --git a/app/src/main/java/com/stoutner/privacybrowser/fragments/DomainSettingsFragment.java b/app/src/main/java/com/stoutner/privacybrowser/fragments/DomainSettingsFragment.java index 45e2b035..474d01c2 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/fragments/DomainSettingsFragment.java +++ b/app/src/main/java/com/stoutner/privacybrowser/fragments/DomainSettingsFragment.java @@ -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 index cceebf02..00000000 --- a/app/src/main/java/com/stoutner/privacybrowser/helpers/BookmarksDatabaseHelper.java +++ /dev/null @@ -1,802 +0,0 @@ -/* - * Copyright © 2016-2022 Soren Stoutner . - * - * This file is part of Privacy Browser Android . - * - * Privacy Browser Android is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Privacy Browser Android is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Privacy Browser Android. If not, see . - */ - -package com.stoutner.privacybrowser.helpers; - -import android.content.ContentValues; -import android.content.Context; -import android.database.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 index 00000000..12753e16 --- /dev/null +++ b/app/src/main/java/com/stoutner/privacybrowser/helpers/BookmarksDatabaseHelper.kt @@ -0,0 +1,748 @@ +/* + * Copyright © 2016-2022 Soren Stoutner . + * + * This file is part of Privacy Browser Android . + * + * Privacy Browser Android is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Privacy Browser Android is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Privacy Browser Android. If not, see . + */ + +package com.stoutner.privacybrowser.helpers + +import android.content.ContentValues +import android.content.Context +import android.database.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 index 011b783f..00000000 --- a/app/src/main/java/com/stoutner/privacybrowser/helpers/CheckPinnedMismatchHelper.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright © 2018-2019,2021-2022 Soren Stoutner . - * - * This file is part of Privacy Browser Android . - * - * Privacy Browser Android is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Privacy Browser Android is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Privacy Browser Android. If not, see . - */ - -package com.stoutner.privacybrowser.helpers; - -import android.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 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 index 00000000..8fa8369f --- /dev/null +++ b/app/src/main/java/com/stoutner/privacybrowser/helpers/CheckPinnedMismatchHelper.kt @@ -0,0 +1,136 @@ +/* + * Copyright © 2018-2019,2021-2022 Soren Stoutner . + * + * This file is part of Privacy Browser Android . + * + * Privacy Browser Android is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Privacy Browser Android is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Privacy Browser Android. If not, see . + */ +package com.stoutner.privacybrowser.helpers + +import android.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 index 2455fb32..00000000 --- a/app/src/main/java/com/stoutner/privacybrowser/helpers/DomainsDatabaseHelper.java +++ /dev/null @@ -1,462 +0,0 @@ -/* - * Copyright © 2017-2022 Soren Stoutner . - * - * This file is part of Privacy Browser Android . - * - * Privacy Browser Android is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Privacy Browser Android is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Privacy Browser Android. If not, see . - */ - -package com.stoutner.privacybrowser.helpers; - -import android.content.ContentValues; -import android.content.Context; -import android.content.SharedPreferences; -import android.database.Cursor; -import android.database.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. - // 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 index 00000000..4fc6a081 --- /dev/null +++ b/app/src/main/java/com/stoutner/privacybrowser/helpers/DomainsDatabaseHelper.kt @@ -0,0 +1,459 @@ +/* + * Copyright © 2017-2022 Soren Stoutner . + * + * This file is part of Privacy Browser Android . + * + * Privacy Browser Android is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Privacy Browser Android is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Privacy Browser Android. If not, see . + */ + +package com.stoutner.privacybrowser.helpers + +import android.content.ContentValues +import android.content.Context +import android.database.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. + // It will be a while before that is supported in Android. + // Although a new table could be created and all the data copied to it, I think I will just leave the old night mode column. It will be wiped out the next time an import is run. + } + + // Upgrade from schema version 12. + 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 diff --git a/app/src/main/java/com/stoutner/privacybrowser/helpers/ImportExportDatabaseHelper.java b/app/src/main/java/com/stoutner/privacybrowser/helpers/ImportExportDatabaseHelper.java index 747123f8..096364f6 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/helpers/ImportExportDatabaseHelper.java +++ b/app/src/main/java/com/stoutner/privacybrowser/helpers/ImportExportDatabaseHelper.java @@ -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(); diff --git a/app/src/main/java/com/stoutner/privacybrowser/views/NestedScrollWebView.kt b/app/src/main/java/com/stoutner/privacybrowser/views/NestedScrollWebView.kt index 1eefe737..9edd3bf2 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/views/NestedScrollWebView.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/views/NestedScrollWebView.kt @@ -208,22 +208,15 @@ class NestedScrollWebView @JvmOverloads constructor(context: Context, attributeS hasPinnedSslCertificate = true } - fun getPinnedSslCertificate(): ArrayList { - // Initialize an array list. - val arrayList = ArrayList() - + fun getPinnedSslCertificate(): Pair, Array> { // 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() { diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml index 027be952..d07e87fb 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -29,89 +29,4 @@ @color/dark_blue_40 @color/red_900 @color/gray_700 - - - #66000000 - - #FF000000 - #11000000 - #22000000 - #33000000 - - #FFE3F2FD - #FFBBDEFB - #FF90CAF9 - #FF64B5F6 - #FF42A5F5 - #FF2196F3 - #FF1E88E5 - #FF1976D2 - #881976D2 - #FF1770C6 - #FF1565C0 - #FF1760B5 - #FF0D47A1 - #FF082B61 - #FF2962FF - - #FFBAD3FB - #FF8AB4F8 - #FF5785C5 - #FF586479 - - #FF607D8B - - #FF1766A6 - #FF14568C - #FF0E3F66 - #FF002A4D - #FF001C33 - - #FF163317 - - #FFFAFAFA - #FFF5F5F5 - #FFEEEEEE - #FFE0E0E0 - #FFC1C1C1 - #FFBDBDBD - #FFB7B7B7 - #FF9E9E9E - #FF757575 - #FF616161 - #FF515151 - #FF424242 - #FF313131 - #FF303030 - #FF2D2D2D - #FF212121 - #FF202020 - - #FFE8F5E9 - #FFC8E6C9 - #FFA5D6A7 - - #FFDCEDC8 - #FF64DD17 - - #FFFFCDD2 - #FFEF9A9A - #FFE53935 - #FFD32F2F - #55D32F2F - #FFC62828 - #FFB71C1C - #FFA21212 - #FF930606 - #FFD50000 - - #00000000 - - #FFFFFFFF - - #FFFFF9C4 - #FFFBC02D - #88FBC02D - #FFF57F17 - #FFFFD600 \ No newline at end of file