Migrate five dialogs to Kotlin. https://redmine.stoutner.com/issues/604
authorSoren Stoutner <soren@stoutner.com>
Fri, 11 Sep 2020 22:51:45 +0000 (15:51 -0700)
committerSoren Stoutner <soren@stoutner.com>
Sat, 12 Sep 2020 16:40:11 +0000 (09:40 -0700)
49 files changed:
app/build.gradle
app/src/free/res/values-pt-rBR/strings.xml [new file with mode: 0644]
app/src/main/assets/de/about_contributors_dark.html
app/src/main/assets/de/about_contributors_light.html
app/src/main/assets/en/about_contributors_dark.html
app/src/main/assets/en/about_contributors_light.html
app/src/main/assets/es/about_contributors_dark.html
app/src/main/assets/es/about_contributors_light.html
app/src/main/assets/fr/about_contributors_dark.html
app/src/main/assets/fr/about_contributors_light.html
app/src/main/assets/it/about_contributors_dark.html
app/src/main/assets/it/about_contributors_light.html
app/src/main/assets/ru/about_contributors_dark.html
app/src/main/assets/ru/about_contributors_light.html
app/src/main/assets/tr/about_contributors_dark.html
app/src/main/assets/tr/about_contributors_light.html
app/src/main/java/com/stoutner/privacybrowser/dialogs/AboutViewSourceDialog.kt
app/src/main/java/com/stoutner/privacybrowser/dialogs/AddDomainDialog.kt
app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateBookmarkDialog.kt
app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateBookmarkFolderDialog.kt
app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateHomeScreenShortcutDialog.kt
app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDatabaseViewDialog.kt
app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDialog.java [deleted file]
app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDialog.kt [new file with mode: 0644]
app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDatabaseViewDialog.java [deleted file]
app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDatabaseViewDialog.kt [new file with mode: 0644]
app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDialog.java [deleted file]
app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDialog.kt [new file with mode: 0644]
app/src/main/java/com/stoutner/privacybrowser/dialogs/FontSizeDialog.java [deleted file]
app/src/main/java/com/stoutner/privacybrowser/dialogs/FontSizeDialog.kt [new file with mode: 0644]
app/src/main/java/com/stoutner/privacybrowser/dialogs/HttpAuthenticationDialog.java [deleted file]
app/src/main/java/com/stoutner/privacybrowser/dialogs/HttpAuthenticationDialog.kt [new file with mode: 0644]
app/src/main/java/com/stoutner/privacybrowser/dialogs/MoveToFolderDialog.java
app/src/main/java/com/stoutner/privacybrowser/dialogs/PinnedMismatchDialog.java
app/src/main/java/com/stoutner/privacybrowser/dialogs/SaveLogcatDialog.java
app/src/main/java/com/stoutner/privacybrowser/dialogs/SslCertificateErrorDialog.java
app/src/main/java/com/stoutner/privacybrowser/dialogs/UrlHistoryDialog.java
app/src/main/java/com/stoutner/privacybrowser/dialogs/ViewRequestDialog.java
app/src/main/java/com/stoutner/privacybrowser/dialogs/ViewSslCertificateDialog.java
app/src/main/java/com/stoutner/privacybrowser/dialogs/WaitingForProxyDialog.java
app/src/main/java/com/stoutner/privacybrowser/fragments/DomainSettingsFragment.java
app/src/main/res/layout/edit_bookmark_databaseview_dialog.xml
app/src/main/res/layout/edit_bookmark_dialog.xml
app/src/main/res/layout/edit_bookmark_folder_databaseview_dialog.xml
app/src/main/res/layout/edit_bookmark_folder_dialog.xml
app/src/main/res/layout/http_authentication_dialog.xml
app/src/main/res/values-pt-rBR/strings.xml [new file with mode: 0644]
app/src/main/res/values/strings.xml
build.gradle

index c00727dfb1767440ac0f1fe72365e536cbfffad0..8e47ef3b4f07957540e3e0030956de6a75027440 100644 (file)
@@ -83,17 +83,17 @@ dependencies {
     implementation 'androidx.cardview:cardview:1.0.0'
     implementation 'androidx.coordinatorlayout:coordinatorlayout:1.1.0'
     implementation "androidx.core:core-ktx:1.3.1"
-    implementation 'androidx.drawerlayout:drawerlayout:1.1.0'
+    implementation 'androidx.drawerlayout:drawerlayout:1.1.1'
     implementation 'androidx.preference:preference:1.1.1'
     implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
     implementation 'androidx.viewpager:viewpager:1.0.0'
     implementation 'androidx.webkit:webkit:1.3.0'
 
-    // Include the Kotlin standard libraries
-    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.72"
+    // Include the Kotlin standard libraries.  This should be the same version number listed in project build.gradle.
+    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.10"
 
     // Include the Google material library.
-    implementation 'com.google.android.material:material:1.2.0'
+    implementation 'com.google.android.material:material:1.2.1'
 
     // Only compile Firebase ads for the free flavor.
     freeImplementation 'com.google.firebase:firebase-ads:19.3.0'
diff --git a/app/src/free/res/values-pt-rBR/strings.xml b/app/src/free/res/values-pt-rBR/strings.xml
new file mode 100644 (file)
index 0000000..c938b6f
--- /dev/null
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  Copyright © 2015-2019 Soren Stoutner <soren@stoutner.com>.
+
+  Translation 2020 Thiago Nazareno Conceição Silva de Jesus <mochileiro2006-trilhas@yahoo.com.br>.  Copyright assigned to Soren Stoutner <soren@stoutner.com>.
+
+  This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+
+  Privacy Browser 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 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.  If not, see <http://www.gnu.org/licenses/>. -->
+
+<resources>
+    <!-- Activities. -->
+    <string name="privacy_browser">Privacy Browser Free</string>
+
+    <!-- Create Home Screen Shortcut Alert Dialog. -->
+    <string name="open_with_privacy_browser">Open with Privacy Browser Free.</string>
+
+    <!-- Ad Consent. -->
+    <string name="ad_consent_text">Privacy Browser Free displays a banner ad on the bottom of the screen.
+        These ads come from Google’s set of commonly used providers and are configured to be non-personalized.
+        \n\nThe standard version of Privacy Browser does not contain app ads.</string>
+    <string name="close_browser">Close Browser</string>
+    <string name="accept_ads">Accept Ads</string>
+</resources>
\ No newline at end of file
index 4afe4615dab113ce7c69ae36b05a08f3caae5463..326c2177d61ba7be19c937b681f0c95deb326dba 100644 (file)
@@ -1,7 +1,7 @@
 <!--
-  Copyright © 2016-2019 Soren Stoutner <soren@stoutner.com>.
+  Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
 
-  Translation 2019 Bernhard G. Keller.  Copyright assigned to Soren Stoutner <soren@stoutner.com>.
+  Translation 2019-2020 Bernhard G. Keller.  Copyright assigned to Soren Stoutner <soren@stoutner.com>.
 
   Translation 2016 Aaron Gerlach <aaron@gerlach.com>.  Copyright assigned to Soren Stoutner <soren@stoutner.com>.
 
@@ -37,6 +37,7 @@
 
         <h3>Mitwirkende</h3>
         Bernhard G. Keller: Deutsch<br/>
+        <a href="mailto:mochileiro2006-trilhas@yahoo.com.br">Thiago Nazareno Conceição Silva de Jesus</a>: Brasilianisches Portugiesisch<br/>
         <a href="mailto:kevinliste@framalistes.org">Kévin LE FLOHIC</a>: Französisch<br/>
         Francesco Buratti: Italienisch<br/>
         Jose A. León: Spanisch
index ea9644688dd8a9cf22647db3f018def9078b56fa..a8a3a6d0364e416c6b4c1df7490eadc5bffd5bcb 100644 (file)
@@ -1,7 +1,7 @@
 <!--
-  Copyright © 2016-2019 Soren Stoutner <soren@stoutner.com>.
+  Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
 
-  Translation 2019 Bernhard G. Keller.  Copyright assigned to Soren Stoutner <soren@stoutner.com>.
+  Translation 2019-2020 Bernhard G. Keller.  Copyright assigned to Soren Stoutner <soren@stoutner.com>.
 
   Translation 2016 Aaron Gerlach <aaron@gerlach.com>.  Copyright assigned to Soren Stoutner <soren@stoutner.com>.
 
@@ -37,6 +37,7 @@
 
         <h3>Mitwirkende</h3>
         Bernhard G. Keller: Deutsch<br/>
+        <a href="mailto:mochileiro2006-trilhas@yahoo.com.br">Thiago Nazareno Conceição Silva de Jesus</a>: Brasilianisches Portugiesisch<br/>
         <a href="mailto:kevinliste@framalistes.org">Kévin LE FLOHIC</a>: Französisch<br/>
         Francesco Buratti: Italienisch<br/>
         Jose A. León: Spanisch
index 4540156b4d2503cd9b0f3a5d9b5a7d0f8003dd7a..5a46175895c255f1d4a63b5318f8ec742b53272b 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-  Copyright © 2016-2019 Soren Stoutner <soren@stoutner.com>.
+  Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
 
   This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
 
@@ -32,6 +32,7 @@
         Hendrik Knackstedt
 
         <h3>Translators</h3>
+        <a href="mailto:mochileiro2006-trilhas@yahoo.com.br">Thiago Nazareno Conceição Silva de Jesus</a>: Brazilian Portuguese<br/>
         <a href="mailto:kevinliste@framalistes.org">Kévin LE FLOHIC</a>: French<br/>
         Bernhard G. Keller: German<br/>
         Francesco Buratti: Italian<br/>
index 52e1e6e4ae84bf19716b676790aa9a03bc3a9ede..284880b3a8c22b442d812478344ebbc1401a83f7 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-  Copyright © 2016-2019 Soren Stoutner <soren@stoutner.com>.
+  Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
 
   This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
 
@@ -32,6 +32,7 @@
         Hendrik Knackstedt
 
         <h3>Translators</h3>
+        <a href="mailto:mochileiro2006-trilhas@yahoo.com.br">Thiago Nazareno Conceição Silva de Jesus</a>: Brazilian Portuguese<br/>
         <a href="mailto:kevinliste@framalistes.org">Kévin LE FLOHIC</a>: French<br/>
         Bernhard G. Keller: German<br/>
         Francesco Buratti: Italian<br/>
index 3c506b2d5792e26ac49107f4760e174c19ef1bfc..bd46d0f66707d9825b7da4b356265a6e7c354bca 100644 (file)
@@ -1,7 +1,7 @@
 <!--
-  Copyright © 2016-2019 Soren Stoutner <soren@stoutner.com>.
+  Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
 
-  Translation 2017-2019 Jose A. León.  Copyright assigned to Soren Stoutner <soren@stoutner.com>.
+  Translation 2017-2020 Jose A. León.  Copyright assigned to Soren Stoutner <soren@stoutner.com>.
 
   This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
 
@@ -35,6 +35,7 @@
 
         <h3>Traductores</h3>
         Jose A. León: Español<br/>
+        <a href="mailto:mochileiro2006-trilhas@yahoo.com.br">Thiago Nazareno Conceição Silva de Jesus</a>: Portugués brasileño<br/>
         <a href="mailto:kevinliste@framalistes.org">Kévin LE FLOHIC</a>: Francés<br/>
         Bernhard G. Keller: Alemán<br/>
         Francesco Buratti: Italiano
index 74804e61679fc88494b8695de6c81d9793f60856..7211860e59b451bcf08889c8c57fa775a990edf3 100644 (file)
@@ -1,7 +1,7 @@
 <!--
-  Copyright © 2016-2019 Soren Stoutner <soren@stoutner.com>.
+  Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
 
-  Translation 2017-2019 Jose A. León.  Copyright assigned to Soren Stoutner <soren@stoutner.com>.
+  Translation 2017-2020 Jose A. León.  Copyright assigned to Soren Stoutner <soren@stoutner.com>.
 
   This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
 
@@ -35,6 +35,7 @@
 
         <h3>Traductores</h3>
         Jose A. León: Español<br/>
+        <a href="mailto:mochileiro2006-trilhas@yahoo.com.br">Thiago Nazareno Conceição Silva de Jesus</a>: Portugués brasileño<br/>
         <a href="mailto:kevinliste@framalistes.org">Kévin LE FLOHIC</a>: Francés<br/>
         Bernhard G. Keller: Alemán<br/>
         Francesco Buratti: Italiano
index 7fca408adda1dea27fdc00bfd24f3aa8d9b98d85..04d5d630aac18f03b8cb75efd0e524d3fa650d95 100644 (file)
@@ -1,7 +1,7 @@
 <!--
-  Copyright © 2016-2019 Soren Stoutner <soren@stoutner.com>.
+  Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
 
-  Translation 2019 Kévin LE FLOHIC <kevinliste@framalistes.org>.  Copyright assigned to Soren Stoutner <soren@stoutner.com>.
+  Translation 2019-2020 Kévin LE FLOHIC <kevinliste@framalistes.org>.  Copyright assigned to Soren Stoutner <soren@stoutner.com>.
 
   This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
 
 
     <body>
         <h3>Développeur principal</h3>
-         <p>Privacy Browser est principalement développé par <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.</p>
+        <p>Privacy Browser est principalement développé par <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.</p>
 
-         <h3>Codeurs</h3>
-         <a href="mailto:lianergoist@vongriffen.dk">Thomas Jensen</a><br/>
-         Hendrik Knackstedt
+        <h3>Codeurs</h3>
+        <a href="mailto:lianergoist@vongriffen.dk">Thomas Jensen</a><br/>
+        Hendrik Knackstedt
 
-         <h3>Traducteurs</h3>
-         <a href="mailto:kevinliste@framalistes.org">Kévin LE FLOHIC</a> : Français<br/>
-         Bernhard G. Keller : Allemand<br/>
-         Francesco Buratti : Italien<br/>
-         Jose A. León : Espagnol
+        <h3>Traducteurs</h3>
+        <a href="mailto:kevinliste@framalistes.org">Kévin LE FLOHIC</a> : Français<br/>
+        <a href="mailto:mochileiro2006-trilhas@yahoo.com.br">Thiago Nazareno Conceição Silva de Jesus</a>: Brazilian Portuguese<br/>
+        Bernhard G. Keller : Allemand<br/>
+        Francesco Buratti : Italien<br/>
+        Jose A. León : Espagnol
 
-         <h3>Anciens traducteurs</h3>
-         Stefan Erhardt: Allemand<br/>
-         <a href="mailto:aaron@gerlach.com">Aaron Gerlach</a> : Allemand
+        <h3>Anciens traducteurs</h3>
+        Stefan Erhardt: Allemand<br/>
+        <a href="mailto:aaron@gerlach.com">Aaron Gerlach</a> : Allemand
 
         <br/>
         <br/>
-         <p>Les contributeurs sont invités à soumettre leurs <a href="https://www.stoutner.com/privacy-browser/contributors/">codes et leurs traductions</a>.</p>
+        <p>Les contributeurs sont invités à soumettre leurs <a href="https://www.stoutner.com/privacy-browser/contributors/">codes et leurs traductions</a>.</p>
     </body>
 </html>
index 45691c731d3efd38468f8bf9fdeadba2d7e4e153..13317ed6fd30b4b5ca66a5ffdbb9d4af3b566647 100644 (file)
@@ -1,7 +1,7 @@
 <!--
-  Copyright © 2016-2019 Soren Stoutner <soren@stoutner.com>.
+  Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
 
-  Translation 2019 Kévin LE FLOHIC <kevinliste@framalistes.org>.  Copyright assigned to Soren Stoutner <soren@stoutner.com>.
+  Translation 2019-2020 Kévin LE FLOHIC <kevinliste@framalistes.org>.  Copyright assigned to Soren Stoutner <soren@stoutner.com>.
 
   This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
 
 
     <body>
         <h3>Développeur principal</h3>
-         <p>Privacy Browser est principalement développé par <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.</p>
+        <p>Privacy Browser est principalement développé par <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.</p>
 
-         <h3>Codeurs</h3>
-         <a href="mailto:lianergoist@vongriffen.dk">Thomas Jensen</a><br/>
-         Hendrik Knackstedt
+        <h3>Codeurs</h3>
+        <a href="mailto:lianergoist@vongriffen.dk">Thomas Jensen</a><br/>
+        Hendrik Knackstedt
 
-         <h3>Traducteurs</h3>
-         <a href="mailto:kevinliste@framalistes.org">Kévin LE FLOHIC</a> : Français<br/>
-         Bernhard G. Keller : Allemand<br/>
-         Francesco Buratti : Italien<br/>
-         Jose A. León : Espagnol
+        <h3>Traducteurs</h3>
+        <a href="mailto:kevinliste@framalistes.org">Kévin LE FLOHIC</a> : Français<br/>
+        <a href="mailto:mochileiro2006-trilhas@yahoo.com.br">Thiago Nazareno Conceição Silva de Jesus</a>: Brazilian Portuguese<br/>
+        Bernhard G. Keller : Allemand<br/>
+        Francesco Buratti : Italien<br/>
+        Jose A. León : Espagnol
 
-         <h3>Anciens traducteurs</h3>
-         Stefan Erhardt: Allemand<br/>
-         <a href="mailto:aaron@gerlach.com">Aaron Gerlach</a> : Allemand
+        <h3>Anciens traducteurs</h3>
+        Stefan Erhardt: Allemand<br/>
+        <a href="mailto:aaron@gerlach.com">Aaron Gerlach</a> : Allemand
 
         <br/>
         <br/>
-         <p>Les contributeurs sont invités à soumettre leurs <a href="https://www.stoutner.com/privacy-browser/contributors/">codes et leurs traductions</a>.</p>
+        <p>Les contributeurs sont invités à soumettre leurs <a href="https://www.stoutner.com/privacy-browser/contributors/">codes et leurs traductions</a>.</p>
     </body>
 </html>
index 93c13decc4bdad3976d815835085038e53d83541..b6c2fa4f9ec03e4188d63685bfb0a81b78df67dc 100644 (file)
@@ -1,7 +1,7 @@
 <!--
-  Copyright © 2017-2019 Soren Stoutner <soren@stoutner.com>.
+  Copyright © 2017-2020 Soren Stoutner <soren@stoutner.com>.
 
-  Translation 2017,2019 Francesco Buratti.  Copyright assigned to Soren Stoutner <soren@stoutner.com>.
+  Translation 2017,2019-2020 Francesco Buratti.  Copyright assigned to Soren Stoutner <soren@stoutner.com>.
 
   This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
 
@@ -35,6 +35,7 @@
 
         <h3>Traduttori</h3>
         Francesco Buratti: Italiano<br/>
+        <a href="mailto:mochileiro2006-trilhas@yahoo.com.br">Thiago Nazareno Conceição Silva de Jesus</a>: Portoghese Brasiliano<br/>
         <a href="mailto:kevinliste@framalistes.org">Kévin LE FLOHIC</a>: Francese<br/>
         Bernhard G. Keller: Tedesco<br/>
         Jose A. León: Spagnolo
index 06813a0296a810e5b9dd3b19a6e8a14363ff0d7c..989faa6ee4fba5140dd79a40253bbd0f2915d755 100644 (file)
@@ -1,7 +1,7 @@
 <!--
-  Copyright © 2017-2019 Soren Stoutner <soren@stoutner.com>.
+  Copyright © 2017-2020 Soren Stoutner <soren@stoutner.com>.
 
-  Translation 2017,2019 Francesco Buratti.  Copyright assigned to Soren Stoutner <soren@stoutner.com>.
+  Translation 2017,2019-2020 Francesco Buratti.  Copyright assigned to Soren Stoutner <soren@stoutner.com>.
 
   This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
 
@@ -35,6 +35,7 @@
 
         <h3>Traduttori</h3>
         Francesco Buratti: Italiano<br/>
+        <a href="mailto:mochileiro2006-trilhas@yahoo.com.br">Thiago Nazareno Conceição Silva de Jesus</a>: Portoghese Brasiliano<br/>
         <a href="mailto:kevinliste@framalistes.org">Kévin LE FLOHIC</a>: Francese<br/>
         Bernhard G. Keller: Tedesco<br/>
         Jose A. León: Spagnolo
index 666f03feb80771c787a6a48fdad74c6608583e9f..d5ab899e3157e0874b569bc71b2f002c1a982797 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-  Copyright © 2016-2019 Soren Stoutner <soren@stoutner.com>.
+  Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
 
   This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
 
@@ -33,6 +33,7 @@
 
         <h3>Переводчики</h3>
         <a href="mailto:kevinliste@framalistes.org">Кевин ЛЕ ФЛОХИК (Kévin LE FLOHIC)</a>: Французский<br/>
+        <a href="mailto:mochileiro2006-trilhas@yahoo.com.br">Тьяго Назарено Консейсао Силва де Хесус (Thiago Nazareno Conceição Silva de Jesus)</a>: бразильский португальский<br/>
         Bernhard G. Keller: немецкий<br/>
         Francesco Buratti: итальянский<br/>
         Jose A. León: испанский
index cf32219faf49b64415144fcb7dea2c33b8bce486..1ab202b571da440b5e9884ac3ea1a826f5bf92e6 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-  Copyright © 2016-2019 Soren Stoutner <soren@stoutner.com>.
+  Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
 
   This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
 
@@ -33,6 +33,7 @@
 
         <h3>Переводчики</h3>
         <a href="mailto:kevinliste@framalistes.org">Кевин ЛЕ ФЛОХИК (Kévin LE FLOHIC)</a>: Французский<br/>
+        <a href="mailto:mochileiro2006-trilhas@yahoo.com.br">Тьяго Назарено Консейсао Силва де Хесус (Thiago Nazareno Conceição Silva de Jesus)</a>: бразильский португальский<br/>
         Bernhard G. Keller: немецкий<br/>
         Francesco Buratti: итальянский<br/>
         Jose A. León: испанский
index a45fcbada7e11ec4a423ef80dd008b1fad75333d..8ffb882bdc6fa49b97584bbbf59dcd5440798844 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-  Copyright © 2016-2019 Soren Stoutner <soren@stoutner.com>.
+  Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
 
   This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
 
@@ -32,6 +32,7 @@
         Hendrik Knackstedt
 
         <h3>Çevirmenler</h3>
+        <a href="mailto:mochileiro2006-trilhas@yahoo.com.br">Thiago Nazareno Conceição Silva de Jesus</a>: Brazilian Portuguese<br/>
         <a href="mailto:kevinliste@framalistes.org">Kévin LE FLOHIC</a>: French<br/>
         Bernhard G. Keller: Almanca<br/>
         Francesco Buratti: İtalyanca<br/>
index 81c3e5267e97571a4bf6441f3a3cd85fa30174ab..44ce32a8b744748e82c7c2ede768244a02ff778e 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-  Copyright © 2016-2019 Soren Stoutner <soren@stoutner.com>.
+  Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
 
   This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
 
@@ -32,6 +32,7 @@
         Hendrik Knackstedt
 
         <h3>Çevirmenler</h3>
+        <a href="mailto:mochileiro2006-trilhas@yahoo.com.br">Thiago Nazareno Conceição Silva de Jesus</a>: Brazilian Portuguese<br/>
         <a href="mailto:kevinliste@framalistes.org">Kévin LE FLOHIC</a>: French<br/>
         Bernhard G. Keller: Almanca<br/>
         Francesco Buratti: İtalyanca<br/>
index d780467f68b79c38ab379dba6a92eb96e1cc0912..937dc3ab775ce11faee3018e03dfc9762444e0dd 100644 (file)
@@ -53,7 +53,7 @@ class AboutViewSourceDialog: DialogFragment() {
         val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
 
         // Get the screenshot preference.
-        val allowScreenshots = sharedPreferences.getBoolean("allow_screenshots", false)
+        val allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false)
 
         // Disable screenshots if not allowed.
         if (!allowScreenshots) {
index e26b64443b79517b3b3923041c35eae94a7330d4..a646a78d907d038f63a1c086239659cf8b76ba92 100644 (file)
@@ -40,13 +40,16 @@ import androidx.preference.PreferenceManager
 import com.stoutner.privacybrowser.R
 import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper
 
+// Declare the class constants.
+private const val URL_STRING = "url_string"
+
 class AddDomainDialog: DialogFragment() {
     // The public interface is used to send information back to the parent activity.
     interface AddDomainListener {
         fun onAddDomain(dialogFragment: DialogFragment)
     }
 
-    // The add domain listener is initialized in `onAttach()` and used in `onCreateDialog()`.
+    // Declare the class variables
     private lateinit var addDomainListener: AddDomainListener
 
     override fun onAttach(context: Context) {
@@ -65,7 +68,7 @@ class AddDomainDialog: DialogFragment() {
             val argumentsBundle = Bundle()
 
             // Store the URL in the bundle.
-            argumentsBundle.putString("url_string", urlString)
+            argumentsBundle.putString(URL_STRING, urlString)
 
             // Create a new instance of the dialog.
             val addDomainDialog = AddDomainDialog()
@@ -78,14 +81,14 @@ class AddDomainDialog: DialogFragment() {
         }
     }
 
-    // `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the alert dialog.
+    // `@SuppressLint("InflateParams")` removes the warning about using `null` as the parent view group when inflating the alert dialog.
     @SuppressLint("InflateParams")
     override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
         // Get the arguments.
         val arguments = requireArguments()
 
         // Get the URL from the bundle.
-        val urlString = arguments.getString("url_string")
+        val urlString = arguments.getString(URL_STRING)
 
         // Use an alert dialog builder to create the alert dialog.
         val dialogBuilder: AlertDialog.Builder = AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog)
@@ -115,7 +118,7 @@ class AddDomainDialog: DialogFragment() {
         val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
 
         // Get the screenshot preference.
-        val allowScreenshots = sharedPreferences.getBoolean("allow_screenshots", false)
+        val allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots), false)
 
         // Disable screenshots if not allowed.
         if (!allowScreenshots) {
index a10e574a5a003ff012849fb6a1ce5e77373e0929..c3871863aae7a3d2ff692e1bdc693487d6565f09 100644 (file)
@@ -41,13 +41,18 @@ import com.stoutner.privacybrowser.R
 
 import java.io.ByteArrayOutputStream
 
+// Declare the class constants.
+private const val URL_STRING = "url_string"
+private const val TITLE = "title"
+private const val FAVORITE_ICON_BYTE_ARRAY = "favorite_icon_byte_array"
+
 class CreateBookmarkDialog: DialogFragment() {
     // The public interface is used to send information back to the parent activity.
     interface CreateBookmarkListener {
         fun onCreateBookmark(dialogFragment: DialogFragment, favoriteIconBitmap: Bitmap)
     }
 
-    // The create bookmark listener is initialized in `onAttach()` and used in `onCreateDialog()`.
+    // Declare the class variables
     private lateinit var createBookmarkListener: CreateBookmarkListener
 
     override fun onAttach(context: Context) {
@@ -61,7 +66,7 @@ class CreateBookmarkDialog: DialogFragment() {
     companion object {
         // `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin.  Also, the function can then be moved out of a companion object and just become a package-level function.
         @JvmStatic
-        fun createBookmark(urlString: String, titleString: String, favoriteIconBitmap: Bitmap): CreateBookmarkDialog {
+        fun createBookmark(urlString: String, title: String, favoriteIconBitmap: Bitmap): CreateBookmarkDialog {
             // Create a favorite icon byte array output stream.
             val favoriteIconByteArrayOutputStream = ByteArrayOutputStream()
 
@@ -75,9 +80,9 @@ class CreateBookmarkDialog: DialogFragment() {
             val argumentsBundle = Bundle()
 
             // Store the variables in the bundle.
-            argumentsBundle.putString("url_string", urlString)
-            argumentsBundle.putString("title_string", titleString)
-            argumentsBundle.putByteArray("favorite_icon_byte_array", favoriteIconByteArray)
+            argumentsBundle.putString(URL_STRING, urlString)
+            argumentsBundle.putString(TITLE, title)
+            argumentsBundle.putByteArray(FAVORITE_ICON_BYTE_ARRAY, favoriteIconByteArray)
 
             // Create a new instance of the dialog.
             val createBookmarkDialog = CreateBookmarkDialog()
@@ -90,16 +95,16 @@ class CreateBookmarkDialog: DialogFragment() {
         }
     }
 
-    // `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the alert dialog.
+    // `@SuppressLint("InflateParams")` removes the warning about using `null` as the parent view group when inflating the alert dialog.
     @SuppressLint("InflateParams")
     override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
         // Get the arguments.
         val arguments = requireArguments()
 
         // Get the contents of the arguments.
-        val urlString = arguments.getString("url_string")
-        val titleString = arguments.getString("title_string")
-        val favoriteIconByteArray = arguments.getByteArray("favorite_icon_byte_array")!!
+        val urlString = arguments.getString(URL_STRING)
+        val title = arguments.getString(TITLE)
+        val favoriteIconByteArray = arguments.getByteArray(FAVORITE_ICON_BYTE_ARRAY)!!
 
         // Convert the favorite icon byte array to a bitmap.
         val favoriteIconBitmap = BitmapFactory.decodeByteArray(favoriteIconByteArray, 0, favoriteIconByteArray.size)
@@ -136,7 +141,7 @@ class CreateBookmarkDialog: DialogFragment() {
         val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
 
         // Get the screenshot preference.
-        val allowScreenshots = sharedPreferences.getBoolean("allow_screenshots", false)
+        val allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false)
 
         // Disable screenshots if not allowed.
         if (!allowScreenshots) {
@@ -151,7 +156,7 @@ class CreateBookmarkDialog: DialogFragment() {
         val createBookmarkUrlEditText = alertDialog.findViewById<EditText>(R.id.create_bookmark_url_edittext)!!
 
         // Set the initial texts for the edit texts.
-        createBookmarkNameEditText.setText(titleString)
+        createBookmarkNameEditText.setText(title)
         createBookmarkUrlEditText.setText(urlString)
 
         // Allow the enter key on the keyboard to create the bookmark from the create bookmark name edit text.
index 83670bc8923b66b1ddb5fc0f8244b6efc042287f..7d4283bfe296657fc82504dac1820c9ceec5f8b2 100644 (file)
@@ -42,13 +42,16 @@ import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper
 
 import java.io.ByteArrayOutputStream
 
+// Declare the class constants.
+private const val FAVORITE_ICON_BYTE_ARRAY = "favorite_icon_byte_array"
+
 class CreateBookmarkFolderDialog: DialogFragment() {
     // The public interface is used to send information back to the parent activity.
     interface CreateBookmarkFolderListener {
         fun onCreateBookmarkFolder(dialogFragment: DialogFragment, favoriteIconBitmap: Bitmap)
     }
 
-    // The create bookmark folder listener is initialized in `onAttach()` and used in `onCreateDialog()`.
+    // Declare the class variables.
     private lateinit var createBookmarkFolderListener: CreateBookmarkFolderListener
 
     override fun onAttach(context: Context) {
@@ -76,7 +79,7 @@ class CreateBookmarkFolderDialog: DialogFragment() {
             val argumentsBundle = Bundle()
 
             // Store the favorite icon in the bundle.
-            argumentsBundle.putByteArray("favorite_icon_byte_array", favoriteIconByteArray)
+            argumentsBundle.putByteArray(FAVORITE_ICON_BYTE_ARRAY, favoriteIconByteArray)
 
             // Create a new instance of the dialog.
             val createBookmarkFolderDialog = CreateBookmarkFolderDialog()
@@ -89,14 +92,14 @@ class CreateBookmarkFolderDialog: DialogFragment() {
         }
     }
 
-    // `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the alert dialog.
+    // `@SuppressLint("InflateParams")` removes the warning about using `null` as the parent view group when inflating the alert dialog.
     @SuppressLint("InflateParams")
     override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
         // Get the arguments.
         val arguments = requireArguments()
 
         // Get the favorite icon byte array.
-        val favoriteIconByteArray = arguments.getByteArray("favorite_icon_byte_array")!!
+        val favoriteIconByteArray = arguments.getByteArray(FAVORITE_ICON_BYTE_ARRAY)!!
 
         // Convert the favorite icon byte array to a bitmap.
         val favoriteIconBitmap = BitmapFactory.decodeByteArray(favoriteIconByteArray, 0, favoriteIconByteArray.size)
@@ -126,7 +129,7 @@ class CreateBookmarkFolderDialog: DialogFragment() {
         val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
 
         // Get the screenshot preference.
-        val allowScreenshots = sharedPreferences.getBoolean("allow_screenshots", false)
+        val allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false)
 
         // Disable screenshots if not allowed.
         if (!allowScreenshots) {
index 69be19d27344fe231a9c3944e75c7423b6ce353c..21e40d47f0a1a19311d87fbf55b056306f2bb69d 100644 (file)
@@ -50,8 +50,13 @@ import com.stoutner.privacybrowser.R
 
 import java.io.ByteArrayOutputStream
 
+// Declare the class constants.
+private const val SHORTCUT_NAME = "shortcut_name"
+private const val URL_STRING = "url_string"
+private const val FAVORITE_ICON_BYTE_ARRAY = "favorite_icon_byte_array"
+
 class CreateHomeScreenShortcutDialog: DialogFragment() {
-    // Define the class variables.
+    // Declare the class views.
     private lateinit var shortcutNameEditText: EditText
     private lateinit var urlEditText: EditText
     private lateinit var openWithPrivacyBrowserRadioButton: RadioButton
@@ -73,9 +78,9 @@ class CreateHomeScreenShortcutDialog: DialogFragment() {
             val argumentsBundle = Bundle()
 
             // Store the variables in the bundle.
-            argumentsBundle.putString("shortcut_name", shortcutName)
-            argumentsBundle.putString("url_string", urlString)
-            argumentsBundle.putByteArray("favorite_icon_byte_array", favoriteIconByteArray)
+            argumentsBundle.putString(SHORTCUT_NAME, shortcutName)
+            argumentsBundle.putString(URL_STRING, urlString)
+            argumentsBundle.putByteArray(FAVORITE_ICON_BYTE_ARRAY, favoriteIconByteArray)
 
             // Create a new instance of the dialog.
             val createHomeScreenShortcutDialog = CreateHomeScreenShortcutDialog()
@@ -88,18 +93,16 @@ class CreateHomeScreenShortcutDialog: DialogFragment() {
         }
     }
 
-    // `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`.
+    // `@SuppressLint("InflateParams")` removes the warning about using `null` as the parent view group when inflating the alert dialog.
     @SuppressLint("InflateParams")
     override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
         // Get the arguments.
         val arguments = requireArguments()
 
-        // Get the strings from the arguments.
-        val initialShortcutName = arguments.getString("shortcut_name")
-        val initialUrlString = arguments.getString("url_string")
-
-        // Get the favorite icon byte array.
-        val favoriteIconByteArray = arguments.getByteArray("favorite_icon_byte_array")!!
+        // Get the variables from the arguments.
+        val initialShortcutName = arguments.getString(SHORTCUT_NAME)
+        val initialUrlString = arguments.getString(URL_STRING)
+        val favoriteIconByteArray = arguments.getByteArray(FAVORITE_ICON_BYTE_ARRAY)!!
 
         // Convert the favorite icon byte array to a bitmap.
         val favoriteIconBitmap = BitmapFactory.decodeByteArray(favoriteIconByteArray, 0, favoriteIconByteArray.size)
@@ -133,7 +136,7 @@ class CreateHomeScreenShortcutDialog: DialogFragment() {
         val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
 
         // Get the screenshot preference.
-        val allowScreenshots = sharedPreferences.getBoolean("allow_screenshots", false)
+        val allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false)
 
         // Disable screenshots if not allowed.
         if (!allowScreenshots) {
index a3c164521bdf22f98b72b80eab8b92f1a467c406..7c2012c6d27dc7471043b0409d6dc66cd2dd6824 100644 (file)
@@ -33,11 +33,19 @@ import android.text.TextWatcher
 import android.view.KeyEvent
 import android.view.View
 import android.view.WindowManager
-import android.widget.*
+import android.widget.AdapterView
 import android.widget.AdapterView.OnItemSelectedListener
+import android.widget.Button
+import android.widget.EditText
+import android.widget.ImageView
+import android.widget.RadioButton
+import android.widget.RadioGroup
+import android.widget.Spinner
+import android.widget.TextView
 
 import androidx.appcompat.app.AlertDialog
 import androidx.core.content.ContextCompat
+import androidx.cursoradapter.widget.ResourceCursorAdapter
 import androidx.fragment.app.DialogFragment
 import androidx.preference.PreferenceManager
 
@@ -47,22 +55,26 @@ import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper
 
 import java.io.ByteArrayOutputStream
 
+// Declare the class constants.
+private const val DATABASE_ID = "database_id"
+private const val FAVORITE_ICON_BYTE_ARRAY = "favorite_icon_byte_array"
+
 class EditBookmarkDatabaseViewDialog: DialogFragment() {
     // The public interface is used to send information back to the parent activity.
     interface EditBookmarkDatabaseViewListener {
         fun onSaveBookmark(dialogFragment: DialogFragment, selectedBookmarkDatabaseId: Int, favoriteIconBitmap: Bitmap)
     }
 
-    // Define the edit bookmark database view listener.
+    // Declare the class variables.
     private lateinit var editBookmarkDatabaseViewListener: EditBookmarkDatabaseViewListener
 
-    // Define the handles for the views that need to be accessed from `updateEditButton()`.
+    // Declare the class views.
     private lateinit var newIconRadioButton: RadioButton
     private lateinit var nameEditText: EditText
     private lateinit var urlEditText: EditText
     private lateinit var folderSpinner: Spinner
     private lateinit var displayOrderEditText: EditText
-    private lateinit var editButton: Button
+    private lateinit var saveButton: Button
 
     override fun onAttach(context: Context) {
         // Run the default commands.
@@ -89,8 +101,8 @@ class EditBookmarkDatabaseViewDialog: DialogFragment() {
             val argumentsBundle = Bundle()
 
             // Store the variables in the bundle.
-            argumentsBundle.putInt("database_id", databaseId)
-            argumentsBundle.putByteArray("favorite_icon_byte_array", favoriteIconByteArray)
+            argumentsBundle.putInt(DATABASE_ID, databaseId)
+            argumentsBundle.putByteArray(FAVORITE_ICON_BYTE_ARRAY, favoriteIconByteArray)
 
             // Create a new instance of the dialog.
             val editBookmarkDatabaseViewDialog = EditBookmarkDatabaseViewDialog()
@@ -103,17 +115,15 @@ class EditBookmarkDatabaseViewDialog: DialogFragment() {
         }
     }
 
-    // `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the alert dialog.
+    // `@SuppressLint("InflateParams")` removes the warning about using `null` as the parent view group when inflating the alert dialog.
     @SuppressLint("InflateParams")
     override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
         // Get the arguments.
         val arguments = requireArguments()
 
-        // Get the bookmark database ID from the bundle.
-        val bookmarkDatabaseId = arguments.getInt("database_id")
-
-        // Get the favorite icon byte array.
-        val favoriteIconByteArray = arguments.getByteArray("favorite_icon_byte_array")!!
+        // Get the variables from the arguments.
+        val bookmarkDatabaseId = arguments.getInt(DATABASE_ID)
+        val favoriteIconByteArray = arguments.getByteArray(FAVORITE_ICON_BYTE_ARRAY)!!
 
         // Convert the favorite icon byte array to a bitmap.
         val favoriteIconBitmap = BitmapFactory.decodeByteArray(favoriteIconByteArray, 0, favoriteIconByteArray.size)
@@ -136,10 +146,10 @@ class EditBookmarkDatabaseViewDialog: DialogFragment() {
         // Set the view.  The parent view is `null` because it will be assigned by the alert dialog.
         dialogBuilder.setView(requireActivity().layoutInflater.inflate(R.layout.edit_bookmark_databaseview_dialog, null))
 
-        // Set the listener for the cancel button.  Using `null` as the listener closes the dialog without doing anything else.
+        // Set the cancel button listener.  Using `null` as the listener closes the dialog without doing anything else.
         dialogBuilder.setNegativeButton(R.string.cancel, null)
 
-        // Set the listener for the save button.
+        // Set the save button listener.
         dialogBuilder.setPositiveButton(R.string.save) { _: DialogInterface, _: Int ->
             // Return the dialog fragment to the parent activity on save.
             editBookmarkDatabaseViewListener.onSaveBookmark(this, bookmarkDatabaseId, favoriteIconBitmap)
@@ -152,7 +162,7 @@ class EditBookmarkDatabaseViewDialog: DialogFragment() {
         val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
 
         // Get the screenshot preference.
-        val allowScreenshots = sharedPreferences.getBoolean("allow_screenshots", false)
+        val allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false)
 
         // Disable screenshots if not allowed.
         if (!allowScreenshots) {
@@ -172,7 +182,7 @@ class EditBookmarkDatabaseViewDialog: DialogFragment() {
         urlEditText = alertDialog.findViewById(R.id.edit_bookmark_url_edittext)!!
         folderSpinner = alertDialog.findViewById(R.id.edit_bookmark_folder_spinner)!!
         displayOrderEditText = alertDialog.findViewById(R.id.edit_bookmark_display_order_edittext)!!
-        editButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE)
+        saveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE)
 
         // Store the current bookmark values.
         val currentBookmarkName = bookmarkCursor.getString(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME))
@@ -280,16 +290,16 @@ class EditBookmarkDatabaseViewDialog: DialogFragment() {
         // Populate the display order edit text.
         displayOrderEditText.setText(bookmarkCursor.getInt(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.DISPLAY_ORDER)).toString())
 
-        // Initially disable the edit button.
-        editButton.isEnabled = false
+        // Initially disable the save button.
+        saveButton.isEnabled = false
 
-        // Update the edit button if the icon selection changes.
+        // Update the save button if the icon selection changes.
         iconRadioGroup.setOnCheckedChangeListener { _: RadioGroup, _: Int ->
-            // Update the edit button.
-            updateEditButton(currentBookmarkName, currentUrl, currentFolderDatabaseId, currentDisplayOrder)
+            // Update the save button.
+            updateSaveButton(currentBookmarkName, currentUrl, currentFolderDatabaseId, currentDisplayOrder)
         }
 
-        // Update the edit button if the bookmark name changes.
+        // Update the save button if the bookmark name changes.
         nameEditText.addTextChangedListener(object: TextWatcher {
             override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
                 // Do nothing.
@@ -300,12 +310,12 @@ class EditBookmarkDatabaseViewDialog: DialogFragment() {
             }
 
             override fun afterTextChanged(s: Editable) {
-                // Update the edit button.
-                updateEditButton(currentBookmarkName, currentUrl, currentFolderDatabaseId, currentDisplayOrder)
+                // Update the Save button.
+                updateSaveButton(currentBookmarkName, currentUrl, currentFolderDatabaseId, currentDisplayOrder)
             }
         })
 
-        // Update the edit button if the URL changes.
+        // Update the save button if the URL changes.
         urlEditText.addTextChangedListener(object: TextWatcher {
             override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
                 // Do nothing.
@@ -316,18 +326,18 @@ class EditBookmarkDatabaseViewDialog: DialogFragment() {
             }
 
             override fun afterTextChanged(s: Editable) {
-                // Update the edit button.
-                updateEditButton(currentBookmarkName, currentUrl, currentFolderDatabaseId, currentDisplayOrder)
+                // Update the save button.
+                updateSaveButton(currentBookmarkName, currentUrl, currentFolderDatabaseId, currentDisplayOrder)
             }
         })
 
         // Wait to set the on item selected listener until the spinner has been inflated.  Otherwise the dialog will crash on restart.
         folderSpinner.post {
-            // Update the edit button if the folder changes.
-            folderSpinner.onItemSelectedListener = object : OnItemSelectedListener {
+            // Update the save button if the folder changes.
+            folderSpinner.onItemSelectedListener = object: OnItemSelectedListener {
                 override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) {
-                    // Update the edit button.
-                    updateEditButton(currentBookmarkName, currentUrl, currentFolderDatabaseId, currentDisplayOrder)
+                    // Update the save button.
+                    updateSaveButton(currentBookmarkName, currentUrl, currentFolderDatabaseId, currentDisplayOrder)
                 }
 
                 override fun onNothingSelected(parent: AdapterView<*>?) {
@@ -336,8 +346,8 @@ class EditBookmarkDatabaseViewDialog: DialogFragment() {
             }
         }
 
-        // Update the edit button if the display order changes.
-        displayOrderEditText.addTextChangedListener(object : TextWatcher {
+        // Update the save button if the display order changes.
+        displayOrderEditText.addTextChangedListener(object: TextWatcher {
             override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
                 // Do nothing.
             }
@@ -347,24 +357,24 @@ class EditBookmarkDatabaseViewDialog: DialogFragment() {
             }
 
             override fun afterTextChanged(s: Editable) {
-                // Update the edit button.
-                updateEditButton(currentBookmarkName, currentUrl, currentFolderDatabaseId, currentDisplayOrder)
+                // Update the save button.
+                updateSaveButton(currentBookmarkName, currentUrl, currentFolderDatabaseId, currentDisplayOrder)
             }
         })
 
         // Allow the enter key on the keyboard to save the bookmark from the bookmark name edit text.
         nameEditText.setOnKeyListener { _: View, keyCode: Int, keyEvent: KeyEvent ->
             // Check the key code, event, and button status.
-            if (keyEvent.action == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER && editButton.isEnabled) {  // The enter key was pressed and the edit button is enabled.
+            if (keyEvent.action == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER && saveButton.isEnabled) {  // The enter key was pressed and the save button is enabled.
                 // Trigger the listener and return the dialog fragment to the parent activity.
-                editBookmarkDatabaseViewListener.onSaveBookmark(this@EditBookmarkDatabaseViewDialog, bookmarkDatabaseId, favoriteIconBitmap)
+                editBookmarkDatabaseViewListener.onSaveBookmark(this, bookmarkDatabaseId, favoriteIconBitmap)
 
                 // Manually dismiss the alert dialog.
                 alertDialog.dismiss()
 
                 // Consume the event.
                 return@setOnKeyListener true
-            } else {  // If any other key was pressed, or if the edit button is currently disabled, do not consume the event.
+            } else {  // If any other key was pressed, or if the save button is currently disabled, do not consume the event.
                 return@setOnKeyListener false
             }
         }
@@ -372,16 +382,16 @@ class EditBookmarkDatabaseViewDialog: DialogFragment() {
         // Allow the enter key on the keyboard to save the bookmark from the URL edit text.
         urlEditText.setOnKeyListener { _: View, keyCode: Int, keyEvent: KeyEvent ->
             // Check the key code, event, and button status.
-            if (keyEvent.action == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER && editButton.isEnabled) {  // The enter key was pressed and the edit button is enabled.
+            if (keyEvent.action == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER && saveButton.isEnabled) {  // The enter key was pressed and the save button is enabled.
                 // Trigger the listener and return the dialog fragment to the parent activity.
-                editBookmarkDatabaseViewListener.onSaveBookmark(this@EditBookmarkDatabaseViewDialog, bookmarkDatabaseId, favoriteIconBitmap)
+                editBookmarkDatabaseViewListener.onSaveBookmark(this, bookmarkDatabaseId, favoriteIconBitmap)
 
                 // Manually dismiss the alert dialog.
                 alertDialog.dismiss()
 
                 // Consume the event.
                 return@setOnKeyListener true
-            } else { // If any other key was pressed, or if the edit button is currently disabled, do not consume the event.
+            } else { // If any other key was pressed, or if the save button is currently disabled, do not consume the event.
                 return@setOnKeyListener false
             }
         }
@@ -389,16 +399,16 @@ class EditBookmarkDatabaseViewDialog: DialogFragment() {
         // Allow the enter key on the keyboard to save the bookmark from the display order edit text.
         displayOrderEditText.setOnKeyListener { _: View, keyCode: Int, keyEvent: KeyEvent ->
             // Check the key code, event, and button status.
-            if (keyEvent.action == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER && editButton.isEnabled) {  // The enter key was pressed and the edit button is enabled.
+            if (keyEvent.action == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER && saveButton.isEnabled) {  // The enter key was pressed and the save button is enabled.
                 // Trigger the listener and return the dialog fragment to the parent activity.
-                editBookmarkDatabaseViewListener.onSaveBookmark(this@EditBookmarkDatabaseViewDialog, bookmarkDatabaseId, favoriteIconBitmap)
+                editBookmarkDatabaseViewListener.onSaveBookmark(this, bookmarkDatabaseId, favoriteIconBitmap)
 
                 // Manually dismiss the alert dialog.
                 alertDialog.dismiss()
 
                 // Consume the event.
                 return@setOnKeyListener true
-            } else { // If any other key was pressed, or if the edit button is currently disabled, do not consume the event.
+            } else { // If any other key was pressed, or if the save button is currently disabled, do not consume the event.
                 return@setOnKeyListener false
             }
         }
@@ -407,7 +417,7 @@ class EditBookmarkDatabaseViewDialog: DialogFragment() {
         return alertDialog
     }
 
-    private fun updateEditButton(currentBookmarkName: String, currentUrl: String, currentFolderDatabaseId: Int, currentDisplayOrder: Int) {
+    private fun updateSaveButton(currentBookmarkName: String, currentUrl: String, currentFolderDatabaseId: Int, currentDisplayOrder: Int) {
         // Get the values from the dialog.
         val newName = nameEditText.text.toString()
         val newUrl = urlEditText.text.toString()
@@ -432,7 +442,7 @@ class EditBookmarkDatabaseViewDialog: DialogFragment() {
         // Is the display order empty?
         val displayOrderNotEmpty = newDisplayOrder.isNotEmpty()
 
-        // Update the enabled status of the edit button.
-        editButton.isEnabled = (iconChanged || nameChanged || urlChanged || folderChanged || displayOrderChanged) && displayOrderNotEmpty
+        // Update the enabled status of the save button.
+        saveButton.isEnabled = (iconChanged || nameChanged || urlChanged || folderChanged || displayOrderChanged) && displayOrderNotEmpty
     }
 }
\ No newline at end of file
diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDialog.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDialog.java
deleted file mode 100644 (file)
index df4a1a1..0000000
+++ /dev/null
@@ -1,318 +0,0 @@
-/*
- * Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
- *
- * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
- *
- * Privacy Browser 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 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.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.stoutner.privacybrowser.dialogs;
-
-import android.annotation.SuppressLint;
-import android.app.Dialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.SharedPreferences;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.os.Bundle;
-import android.preference.PreferenceManager;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.ImageView;
-import android.widget.RadioButton;
-import android.widget.RadioGroup;
-
-import androidx.annotation.NonNull;
-import androidx.appcompat.app.AlertDialog;
-import androidx.fragment.app.DialogFragment;
-
-import com.stoutner.privacybrowser.R;
-import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper;
-
-import java.io.ByteArrayOutputStream;
-
-public class EditBookmarkDialog extends DialogFragment {
-    // Define the edit bookmark listener.
-    private EditBookmarkListener editBookmarkListener;
-
-    // The public interface is used to send information back to the parent activity.
-    public interface EditBookmarkListener {
-        void onSaveBookmark(DialogFragment dialogFragment, int selectedBookmarkDatabaseId, Bitmap favoriteIconBitmap);
-    }
-
-    public void onAttach(@NonNull Context context) {
-        // Run the default commands.
-        super.onAttach(context);
-
-        // Get a handle for `EditBookmarkListener` from the launching context.
-        editBookmarkListener = (EditBookmarkListener) context;
-    }
-
-    // Store the database ID in the arguments bundle.
-    public static EditBookmarkDialog bookmarkDatabaseId(int databaseId, Bitmap favoriteIconBitmap) {
-        // Create a favorite icon byte array output stream.
-        ByteArrayOutputStream favoriteIconByteArrayOutputStream = new ByteArrayOutputStream();
-
-        // Convert the favorite icon to a PNG and place it in the byte array output stream.  `0` is for lossless compression (the only option for a PNG).
-        favoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, favoriteIconByteArrayOutputStream);
-
-        // Convert the byte array output stream to a byte array.
-        byte[] favoriteIconByteArray = favoriteIconByteArrayOutputStream.toByteArray();
-
-        // Create an arguments bundle.
-        Bundle argumentsBundle = new Bundle();
-
-        // Store the variables in the bundle.
-        argumentsBundle.putInt("database_id", databaseId);
-        argumentsBundle.putByteArray("favorite_icon_byte_array", favoriteIconByteArray);
-
-        // Create a new instance of the dialog.
-        EditBookmarkDialog editBookmarkDialog = new EditBookmarkDialog();
-
-        // Add the arguments bundle to the dialog.
-        editBookmarkDialog.setArguments(argumentsBundle);
-
-        // Return the new dialog.
-        return editBookmarkDialog;
-    }
-
-    // `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`.
-    @SuppressLint("InflateParams")
-    @Override
-    @NonNull
-    public Dialog onCreateDialog(Bundle savedInstanceState) {
-        // Get the arguments.
-        Bundle arguments = getArguments();
-
-        // Remove the incorrect lint warning below that the arguments might be null.
-        assert arguments != null;
-
-        // Store the bookmark database ID in the class variable.
-        int selectedBookmarkDatabaseId = arguments.getInt("database_id");
-
-        // Get the favorite icon byte array.
-        byte[] favoriteIconByteArray = arguments.getByteArray("favorite_icon_byte_array");
-
-        // Remove the incorrect lint warning below that the favorite icon byte array might be null.
-        assert favoriteIconByteArray != null;
-
-        // Convert the favorite icon byte array to a bitmap.
-        Bitmap favoriteIconBitmap = BitmapFactory.decodeByteArray(favoriteIconByteArray, 0, favoriteIconByteArray.length);
-
-        // Initialize the database helper.  The `0` specifies a database version, but that is ignored and set instead using a constant in `BookmarksDatabaseHelper`.
-        BookmarksDatabaseHelper bookmarksDatabaseHelper = new BookmarksDatabaseHelper(getContext(), null, null, 0);
-
-        // Get a `Cursor` with the selected bookmark and move it to the first position.
-        Cursor bookmarkCursor = bookmarksDatabaseHelper.getBookmark(selectedBookmarkDatabaseId);
-        bookmarkCursor.moveToFirst();
-
-        // Use an alert dialog builder to create the alert dialog.
-        AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog);
-
-        // Set the title.
-        dialogBuilder.setTitle(R.string.edit_bookmark);
-
-        // Remove the incorrect lint warning that `getActivity().getLayoutInflater()` might be null.
-        assert getActivity() != null;
-
-        // Set the view.  The parent view is null because it will be assigned by the alert dialog.
-        dialogBuilder.setView(getActivity().getLayoutInflater().inflate(R.layout.edit_bookmark_dialog, null));
-
-        // Set the cancel button listener.
-        dialogBuilder.setNegativeButton(R.string.cancel, (DialogInterface dialog, int which) -> {
-            // Do nothing.  The alert dialog will close automatically.
-        });
-
-        // Set the save button listener.
-        dialogBuilder.setPositiveButton(R.string.save, (DialogInterface dialog, int which) -> {
-            // Return the dialog fragment to the parent activity.
-            editBookmarkListener.onSaveBookmark(this, selectedBookmarkDatabaseId, favoriteIconBitmap);
-        });
-
-        // Create an alert dialog from the builder.
-        final AlertDialog alertDialog = dialogBuilder.create();
-
-        // remove the incorrect lint warning below that `getWindow().addFlags()` might be null.
-        assert alertDialog.getWindow() != null;
-
-        // Get a handle for the shared preferences.
-        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());
-
-        // Get the screenshot preference.
-        boolean allowScreenshots = sharedPreferences.getBoolean("allow_screenshots", false);
-
-        // Disable screenshots if not allowed.
-        if (!allowScreenshots) {
-            alertDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
-        }
-
-        // The alert dialog must be shown before items in the layout can be modified.
-        alertDialog.show();
-
-        // Get handles for the layout items.
-        RadioGroup iconRadioGroup = alertDialog.findViewById(R.id.edit_bookmark_icon_radiogroup);
-        ImageView currentIconImageView = alertDialog.findViewById(R.id.edit_bookmark_current_icon);
-        ImageView newFavoriteIconImageView = alertDialog.findViewById(R.id.edit_bookmark_webpage_favorite_icon);
-        EditText nameEditText = alertDialog.findViewById(R.id.edit_bookmark_name_edittext);
-        EditText urlEditText = alertDialog.findViewById(R.id.edit_bookmark_url_edittext);
-        Button editButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
-
-        // Remove the incorrect lint warnings below that the views might be null.
-        assert iconRadioGroup != null;
-        assert currentIconImageView != null;
-        assert newFavoriteIconImageView != null;
-        assert nameEditText != null;
-        assert urlEditText != null;
-
-        // Get the current favorite icon byte array from the cursor.
-        byte[] currentIconByteArray = bookmarkCursor.getBlob(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.FAVORITE_ICON));
-
-        // Convert the byte array to a bitmap beginning at the first byte and ending at the last.
-        Bitmap currentIconBitmap = BitmapFactory.decodeByteArray(currentIconByteArray, 0, currentIconByteArray.length);
-
-        // Display the current icon bitmap.
-        currentIconImageView.setImageBitmap(currentIconBitmap);
-
-        // Set the new favorite icon bitmap.
-        newFavoriteIconImageView.setImageBitmap(favoriteIconBitmap);
-
-        // Store the current bookmark name and URL.
-        String currentName = bookmarkCursor.getString(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME));
-        String currentUrl = bookmarkCursor.getString(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_URL));
-
-        // Populate the edit texts.
-        nameEditText.setText(currentName);
-        urlEditText.setText(currentUrl);
-
-        // Initially disable the edit button.
-        editButton.setEnabled(false);
-
-        // Update the edit button if the icon selection changes.
-        iconRadioGroup.setOnCheckedChangeListener((RadioGroup group, int checkedId) -> {
-            // Update the edit button.
-            updateEditButton(alertDialog, currentName, currentUrl);
-        });
-
-        // Update the edit button if the bookmark name changes.
-        nameEditText.addTextChangedListener(new TextWatcher() {
-            @Override
-            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
-                // Do nothing.
-            }
-
-            @Override
-            public void onTextChanged(CharSequence s, int start, int before, int count) {
-                // Do nothing.
-            }
-
-            @Override
-            public void afterTextChanged(Editable s) {
-                // Update the edit button.
-                updateEditButton(alertDialog, currentName, currentUrl);
-            }
-        });
-
-        // Update the edit button if the URL changes.
-        urlEditText.addTextChangedListener(new TextWatcher() {
-            @Override
-            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
-                // Do nothing.
-            }
-
-            @Override
-            public void onTextChanged(CharSequence s, int start, int before, int count) {
-                // Do nothing.
-            }
-
-            @Override
-            public void afterTextChanged(Editable s) {
-                // Update the edit button.
-                updateEditButton(alertDialog, currentName, currentUrl);
-            }
-        });
-
-        // Allow the enter key on the keyboard to save the bookmark from the bookmark name edit text.
-        nameEditText.setOnKeyListener((View v, int keyCode, KeyEvent event) -> {
-            // Save the bookmark if the event is a key-down on the "enter" button.
-            if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER) && editButton.isEnabled()) {  // The enter key was pressed and the edit button is enabled.
-                // Trigger the `Listener` and return the `DialogFragment` to the parent activity.
-                editBookmarkListener.onSaveBookmark(this, selectedBookmarkDatabaseId, favoriteIconBitmap);
-
-                // Manually dismiss `alertDialog`.
-                alertDialog.dismiss();
-
-                // Consume the event.
-                return true;
-            } else {  // If any other key was pressed, or if the edit button is currently disabled, do not consume the event.
-                return false;
-            }
-        });
-
-        // Allow the enter key on the keyboard to save the bookmark from the URL edit text.
-        urlEditText.setOnKeyListener((View v, int keyCode, KeyEvent event) -> {
-            // Save the bookmark if the event is a key-down on the "enter" button.
-            if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER) && editButton.isEnabled()) {  // The enter key was pressed and the edit button is enabled.
-                // Trigger the `Listener` and return the DialogFragment to the parent activity.
-                editBookmarkListener.onSaveBookmark(this, selectedBookmarkDatabaseId, favoriteIconBitmap);
-
-                // Manually dismiss the alert dialog.
-                alertDialog.dismiss();
-
-                // Consume the event.
-                return true;
-            } else { // If any other key was pressed, or if the edit button is currently disabled, do not consume the event.
-                return false;
-            }
-        });
-
-        // Return the alert dialog.
-        return alertDialog;
-    }
-
-    private void updateEditButton(AlertDialog alertdialog, String currentName, String currentUrl) {
-        // Get handles for the views.
-        EditText nameEditText = alertdialog.findViewById(R.id.edit_bookmark_name_edittext);
-        EditText urlEditText = alertdialog.findViewById(R.id.edit_bookmark_url_edittext);
-        RadioButton newIconRadioButton = alertdialog.findViewById(R.id.edit_bookmark_webpage_favorite_icon_radiobutton);
-        Button editButton = alertdialog.getButton(AlertDialog.BUTTON_POSITIVE);
-
-        // Remove the incorrect lint warnings below that the views might be null.
-        assert nameEditText != null;
-        assert urlEditText != null;
-        assert newIconRadioButton != null;
-
-        // Get the text from the edit texts.
-        String newName = nameEditText.getText().toString();
-        String newUrl = urlEditText.getText().toString();
-
-        // Has the favorite icon changed?
-        boolean iconChanged = newIconRadioButton.isChecked();
-
-        // Has the name changed?
-        boolean nameChanged = !newName.equals(currentName);
-
-        // Has the URL changed?
-        boolean urlChanged = !newUrl.equals(currentUrl);
-
-        // Update the enabled status of the edit button.
-        editButton.setEnabled(iconChanged || nameChanged || urlChanged);
-    }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDialog.kt b/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDialog.kt
new file mode 100644 (file)
index 0000000..efe1c0d
--- /dev/null
@@ -0,0 +1,288 @@
+/*
+ * Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+ *
+ * Privacy Browser 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 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.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.stoutner.privacybrowser.dialogs
+
+import android.annotation.SuppressLint
+import android.app.Dialog
+import android.content.Context
+import android.content.DialogInterface
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.os.Bundle
+import android.text.Editable
+import android.text.TextWatcher
+import android.view.KeyEvent
+import android.view.View
+import android.view.WindowManager
+import android.widget.Button
+import android.widget.EditText
+import android.widget.ImageView
+import android.widget.RadioButton
+import android.widget.RadioGroup
+
+import androidx.appcompat.app.AlertDialog
+import androidx.fragment.app.DialogFragment
+import androidx.preference.PreferenceManager
+
+import com.stoutner.privacybrowser.R
+import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper
+
+import java.io.ByteArrayOutputStream
+
+// Declare the class constants.
+private const val DATABASE_ID = "database_id"
+private const val FAVORITE_ICON_BYTE_ARRAY = "favorite_icon_byte_array"
+
+class EditBookmarkDialog: DialogFragment() {
+    // The public interface is used to send information back to the parent activity.
+    interface EditBookmarkListener {
+        fun onSaveBookmark(dialogFragment: DialogFragment, selectedBookmarkDatabaseId: Int, favoriteIconBitmap: Bitmap)
+    }
+
+    // Declare the class variables.
+    private lateinit var editBookmarkListener: EditBookmarkListener
+
+    // Declare the class views.
+    private lateinit var nameEditText: EditText
+    private lateinit var urlEditText: EditText
+    private lateinit var newIconRadioButton: RadioButton
+    private lateinit var saveButton: Button
+
+    override fun onAttach(context: Context) {
+        // Run the default commands.
+        super.onAttach(context)
+
+        // Get a handle for the edit bookmark listener from the launching context.
+        editBookmarkListener = context as EditBookmarkListener
+    }
+
+    companion object {
+        // `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin.  Also, the function can then be moved out of a companion object and just become a package-level function.
+        @JvmStatic
+        fun bookmarkDatabaseId(databaseId: Int, favoriteIconBitmap: Bitmap): EditBookmarkDialog {
+            // Create a favorite icon byte array output stream.
+            val favoriteIconByteArrayOutputStream = ByteArrayOutputStream()
+
+            // Convert the favorite icon to a PNG and place it in the byte array output stream.  `0` is for lossless compression (the only option for a PNG).
+            favoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, favoriteIconByteArrayOutputStream)
+
+            // Convert the byte array output stream to a byte array.
+            val favoriteIconByteArray = favoriteIconByteArrayOutputStream.toByteArray()
+
+            // Create an arguments bundle.
+            val argumentsBundle = Bundle()
+
+            // Store the variables in the bundle.
+            argumentsBundle.putInt(DATABASE_ID, databaseId)
+            argumentsBundle.putByteArray(FAVORITE_ICON_BYTE_ARRAY, favoriteIconByteArray)
+
+            // Create a new instance of the dialog.
+            val editBookmarkDialog = EditBookmarkDialog()
+
+            // Add the arguments bundle to the dialog.
+            editBookmarkDialog.arguments = argumentsBundle
+
+            // Return the new dialog.
+            return editBookmarkDialog
+        }
+    }
+
+    // `@SuppressLint("InflateParams")` removes the warning about using `null` as the parent view group when inflating the alert dialog.
+    @SuppressLint("InflateParams")
+    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+        // Get the arguments.
+        val arguments = requireArguments()
+
+        // Get the variables from the arguments.
+        val selectedBookmarkDatabaseId = arguments.getInt(DATABASE_ID)
+        val favoriteIconByteArray = arguments.getByteArray(FAVORITE_ICON_BYTE_ARRAY)!!
+
+        // 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)
+
+        // Get a cursor with the selected bookmark.
+        val bookmarkCursor = bookmarksDatabaseHelper.getBookmark(selectedBookmarkDatabaseId)
+
+        // Move the cursor to the first position.
+        bookmarkCursor.moveToFirst()
+
+        // Use an alert dialog builder to create the alert dialog.
+        val dialogBuilder = AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog)
+
+        // Set the title.
+        dialogBuilder.setTitle(R.string.edit_bookmark)
+
+        // Set the view.  The parent view is null because it will be assigned by the alert dialog.
+        dialogBuilder.setView(requireActivity().layoutInflater.inflate(R.layout.edit_bookmark_dialog, null))
+
+        // Set the cancel button listener.  Using `null` as the listener closes the dialog without doing anything else.
+        dialogBuilder.setNegativeButton(R.string.cancel, null)
+
+        // Set the save button listener.
+        dialogBuilder.setPositiveButton(R.string.save) { _: DialogInterface?, _: Int ->
+            // Return the dialog fragment to the parent activity.
+            editBookmarkListener.onSaveBookmark(this, selectedBookmarkDatabaseId, favoriteIconBitmap)
+        }
+
+        // Create an alert dialog from the builder.
+        val alertDialog = dialogBuilder.create()
+
+        // Get a handle for the shared preferences.
+        val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
+
+        // Get the screenshot preference.
+        val allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false)
+
+        // Disable screenshots if not allowed.
+        if (!allowScreenshots) {
+            alertDialog.window!!.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
+        }
+
+        // The alert dialog must be shown before items in the layout can be modified.
+        alertDialog.show()
+
+        // Get handles for the layout items.
+        val iconRadioGroup = alertDialog.findViewById<RadioGroup>(R.id.edit_bookmark_icon_radiogroup)!!
+        val currentIconImageView = alertDialog.findViewById<ImageView>(R.id.edit_bookmark_current_icon)!!
+        val newFavoriteIconImageView = alertDialog.findViewById<ImageView>(R.id.edit_bookmark_webpage_favorite_icon)!!
+        newIconRadioButton = alertDialog.findViewById(R.id.edit_bookmark_webpage_favorite_icon_radiobutton)!!
+        nameEditText = alertDialog.findViewById(R.id.edit_bookmark_name_edittext)!!
+        urlEditText = alertDialog.findViewById(R.id.edit_bookmark_url_edittext)!!
+        saveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE)
+
+        // Get the current favorite icon byte array from the cursor.
+        val currentIconByteArray = bookmarkCursor.getBlob(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.FAVORITE_ICON))
+
+        // Convert the byte array to a bitmap beginning at the first byte and ending at the last.
+        val currentIconBitmap = BitmapFactory.decodeByteArray(currentIconByteArray, 0, currentIconByteArray.size)
+
+        // Display the current icon bitmap.
+        currentIconImageView.setImageBitmap(currentIconBitmap)
+
+        // Set the new favorite icon bitmap.
+        newFavoriteIconImageView.setImageBitmap(favoriteIconBitmap)
+
+        // Store the current bookmark name and URL.
+        val currentName = bookmarkCursor.getString(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME))
+        val currentUrl = bookmarkCursor.getString(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_URL))
+
+        // Populate the edit texts.
+        nameEditText.setText(currentName)
+        urlEditText.setText(currentUrl)
+
+        // Initially disable the save button.
+        saveButton.isEnabled = false
+
+        // Update the save button if the icon selection changes.
+        iconRadioGroup.setOnCheckedChangeListener { _: RadioGroup?, _: Int ->
+            // Update the save button.
+            updateSaveButton(currentName, currentUrl)
+        }
+
+        // Update the save button if the bookmark name changes.
+        nameEditText.addTextChangedListener(object: TextWatcher {
+            override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
+                // Do nothing.
+            }
+
+            override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
+                // Do nothing.
+            }
+
+            override fun afterTextChanged(s: Editable) {
+                // Update the save button.
+                updateSaveButton(currentName, currentUrl)
+            }
+        })
+
+        // Update the save button if the URL changes.
+        urlEditText.addTextChangedListener(object: TextWatcher {
+            override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
+                // Do nothing.
+            }
+
+            override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
+                // Do nothing.
+            }
+
+            override fun afterTextChanged(s: Editable) {
+                // Update the edit button.
+                updateSaveButton(currentName, currentUrl)
+            }
+        })
+
+        // Allow the enter key on the keyboard to save the bookmark from the bookmark name edit text.
+        nameEditText.setOnKeyListener { _: View?, keyCode: Int, event: KeyEvent ->
+            // Check the key code, event, and button status.
+            if (event.action == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER && saveButton.isEnabled) {  // The enter key was pressed and the save button is enabled.
+                // Trigger the listener and return the dialog fragment to the parent activity.
+                editBookmarkListener.onSaveBookmark(this, selectedBookmarkDatabaseId, favoriteIconBitmap)
+
+                // Manually dismiss the alert dialog.
+                alertDialog.dismiss()
+
+                // Consume the event.
+                return@setOnKeyListener true
+            } else {  // If any other key was pressed, or if the save button is currently disabled, do not consume the event.
+                return@setOnKeyListener false
+            }
+        }
+
+        // Allow the enter key on the keyboard to save the bookmark from the URL edit text.
+        urlEditText.setOnKeyListener { _: View?, keyCode: Int, event: KeyEvent ->
+            // Check the key code, event, and button status.
+            if (event.action == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER && saveButton.isEnabled) {  // The enter key was pressed and the save button is enabled.
+                // Trigger the listener and return the dialog fragment to the parent activity.
+                editBookmarkListener.onSaveBookmark(this, selectedBookmarkDatabaseId, favoriteIconBitmap)
+
+                // Manually dismiss the alert dialog.
+                alertDialog.dismiss()
+
+                // Consume the event.
+                return@setOnKeyListener true
+            } else { // If any other key was pressed, or if the save button is currently disabled, do not consume the event.
+                return@setOnKeyListener false
+            }
+        }
+
+        // Return the alert dialog.
+        return alertDialog
+    }
+
+    private fun updateSaveButton(currentName: String, currentUrl: String) {
+        // Get the text from the edit texts.
+        val newName = nameEditText.text.toString()
+        val newUrl = urlEditText.text.toString()
+
+        // Has the favorite icon changed?
+        val iconChanged = newIconRadioButton.isChecked
+
+        // Has the name changed?
+        val nameChanged = newName != currentName
+
+        // Has the URL changed?
+        val urlChanged = newUrl != currentUrl
+
+        // Update the enabled status of the save button.
+        saveButton.isEnabled = iconChanged || nameChanged || urlChanged
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDatabaseViewDialog.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDatabaseViewDialog.java
deleted file mode 100644 (file)
index 7960c68..0000000
+++ /dev/null
@@ -1,480 +0,0 @@
-/*
- * Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
- *
- * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
- *
- * Privacy Browser 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 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.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.stoutner.privacybrowser.dialogs;
-
-import android.annotation.SuppressLint;
-import android.app.Dialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.SharedPreferences;
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.database.MatrixCursor;
-import android.database.MergeCursor;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.os.Bundle;
-import android.preference.PreferenceManager;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.AdapterView;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.ImageView;
-import android.widget.RadioButton;
-import android.widget.RadioGroup;
-import android.widget.ResourceCursorAdapter;
-import android.widget.Spinner;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.appcompat.app.AlertDialog;
-import androidx.core.content.ContextCompat;
-import androidx.fragment.app.DialogFragment;  // The AndroidX dialog fragment must be used or an error is produced on API <=22.
-
-import com.stoutner.privacybrowser.R;
-import com.stoutner.privacybrowser.activities.BookmarksDatabaseViewActivity;
-import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper;
-
-import java.io.ByteArrayOutputStream;
-
-public class EditBookmarkFolderDatabaseViewDialog extends DialogFragment {
-    // Define the edit bookmark folder database view listener.
-    private EditBookmarkFolderDatabaseViewListener editBookmarkFolderDatabaseViewListener;
-
-
-    // The public interface is used to send information back to the parent activity.
-    public interface EditBookmarkFolderDatabaseViewListener {
-        void onSaveBookmarkFolder(DialogFragment dialogFragment, int selectedFolderDatabaseId, Bitmap favoriteIconBitmap);
-    }
-
-    public void onAttach(@NonNull Context context) {
-        // Run the default commands.
-        super.onAttach(context);
-
-        // Get a handle for `EditBookmarkDatabaseViewListener` from the launching context.
-        editBookmarkFolderDatabaseViewListener = (EditBookmarkFolderDatabaseViewListener) context;
-    }
-
-
-    public static EditBookmarkFolderDatabaseViewDialog folderDatabaseId(int databaseId, Bitmap favoriteIconBitmap) {
-        // Create a favorite icon byte array output stream.
-        ByteArrayOutputStream favoriteIconByteArrayOutputStream = new ByteArrayOutputStream();
-
-        // Convert the favorite icon to a PNG and place it in the byte array output stream.  `0` is for lossless compression (the only option for a PNG).
-        favoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, favoriteIconByteArrayOutputStream);
-
-        // Convert the byte array output stream to a byte array.
-        byte[] favoriteIconByteArray = favoriteIconByteArrayOutputStream.toByteArray();
-
-        // Create an arguments bundle.
-        Bundle argumentsBundle = new Bundle();
-
-        // Store the variables in the bundle.
-        argumentsBundle.putInt("database_id", databaseId);
-        argumentsBundle.putByteArray("favorite_icon_byte_array", favoriteIconByteArray);
-
-        // Create a new instance of the dialog.
-        EditBookmarkFolderDatabaseViewDialog editBookmarkFolderDatabaseViewDialog = new EditBookmarkFolderDatabaseViewDialog();
-
-        // Add the arguments bundle to the dialog.
-        editBookmarkFolderDatabaseViewDialog.setArguments(argumentsBundle);
-
-        // Return the new dialog.
-        return editBookmarkFolderDatabaseViewDialog;
-    }
-
-    // `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`.
-    @SuppressLint("InflateParams")
-    @Override
-    @NonNull
-    public Dialog onCreateDialog(Bundle savedInstanceState) {
-        // Get the arguments.
-        Bundle arguments = getArguments();
-
-        // Remove the incorrect lint warning below that the arguments might be null.
-        assert arguments != null;
-
-        // Get the bookmark database ID from the bundle.
-        int folderDatabaseId = getArguments().getInt("database_id");
-
-        // Get the favorite icon byte array.
-        byte[] favoriteIconByteArray = arguments.getByteArray("favorite_icon_byte_array");
-
-        // Remove the incorrect lint warning below that the favorite icon byte array might be null.
-        assert favoriteIconByteArray != null;
-
-        // Convert the favorite icon byte array to a bitmap.
-        Bitmap favoriteIconBitmap = BitmapFactory.decodeByteArray(favoriteIconByteArray, 0, favoriteIconByteArray.length);
-
-        // Initialize the database helper.   The `0` specifies a database version, but that is ignored and set instead using a constant in `BookmarksDatabaseHelper`.
-        BookmarksDatabaseHelper bookmarksDatabaseHelper = new BookmarksDatabaseHelper(getContext(), null, null, 0);
-
-        // Get a `Cursor` with the selected bookmark and move it to the first position.
-        Cursor folderCursor = bookmarksDatabaseHelper.getBookmark(folderDatabaseId);
-        folderCursor.moveToFirst();
-
-        // Use an alert dialog builder to create the alert dialog.
-        AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog);
-
-        // Set the title.
-        dialogBuilder.setTitle(R.string.edit_folder);
-
-        // Remove the incorrect lint warning that `getActivity()` might be null.
-        assert getActivity() != null;
-
-        // Set the view.  The parent view is `null` because it will be assigned by `AlertDialog`.
-        dialogBuilder.setView(getActivity().getLayoutInflater().inflate(R.layout.edit_bookmark_folder_databaseview_dialog, null));
-
-        // Set the listener for the negative button.
-        dialogBuilder.setNegativeButton(R.string.cancel, (DialogInterface dialog, int which) -> {
-            // Do nothing.  The `AlertDialog` will close automatically.
-        });
-
-        // Set the listener fo the positive button.
-        dialogBuilder.setPositiveButton(R.string.save, (DialogInterface dialog, int which) -> {
-            // Return the `DialogFragment` to the parent activity on save.
-            editBookmarkFolderDatabaseViewListener.onSaveBookmarkFolder(this, folderDatabaseId, favoriteIconBitmap);
-        });
-
-        // Create an alert dialog from the alert dialog builder.
-        final AlertDialog alertDialog = dialogBuilder.create();
-
-        // Remove the warning below that `getWindow()` might be null.
-        assert alertDialog.getWindow() != null;
-
-        // Get a handle for the shared preferences.
-        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());
-
-        // Get the screenshot preference.
-        boolean allowScreenshots = sharedPreferences.getBoolean("allow_screenshots", false);
-
-        // Disable screenshots if not allowed.
-        if (!allowScreenshots) {
-            alertDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
-        }
-
-        // The alert dialog must be shown before items in the layout can be modified.
-        alertDialog.show();
-
-        // Get handles for the layout items.
-        TextView databaseIdTextView = alertDialog.findViewById(R.id.edit_folder_database_id_textview);
-        RadioGroup iconRadioGroup = alertDialog.findViewById(R.id.edit_folder_icon_radiogroup);
-        ImageView currentIconImageView = alertDialog.findViewById(R.id.edit_folder_current_icon_imageview);
-        ImageView newFavoriteIconImageView = alertDialog.findViewById(R.id.edit_folder_webpage_favorite_icon_imageview);
-        EditText nameEditText = alertDialog.findViewById(R.id.edit_folder_name_edittext);
-        Spinner folderSpinner = alertDialog.findViewById(R.id.edit_folder_parent_folder_spinner);
-        EditText displayOrderEditText = alertDialog.findViewById(R.id.edit_folder_display_order_edittext);
-        Button editButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
-
-        // Remove the incorrect lint warnings below that the views might be null.
-        assert databaseIdTextView != null;
-        assert iconRadioGroup != null;
-        assert currentIconImageView != null;
-        assert newFavoriteIconImageView != null;
-        assert nameEditText != null;
-        assert folderSpinner != null;
-        assert displayOrderEditText != null;
-
-        // Store the current folder values.
-        String currentFolderName = folderCursor.getString(folderCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME));
-        int currentDisplayOrder = folderCursor.getInt(folderCursor.getColumnIndex(BookmarksDatabaseHelper.DISPLAY_ORDER));
-        String parentFolder = folderCursor.getString(folderCursor.getColumnIndex(BookmarksDatabaseHelper.PARENT_FOLDER));
-
-        // Set the database ID.
-        databaseIdTextView.setText(String.valueOf(folderCursor.getInt(folderCursor.getColumnIndex(BookmarksDatabaseHelper._ID))));
-
-        // Get the current favorite icon byte array from the `Cursor`.
-        byte[] currentIconByteArray = folderCursor.getBlob(folderCursor.getColumnIndex(BookmarksDatabaseHelper.FAVORITE_ICON));
-
-        // Convert the byte array to a `Bitmap` beginning at the first byte and ending at the last.
-        Bitmap currentIconBitmap = BitmapFactory.decodeByteArray(currentIconByteArray, 0, currentIconByteArray.length);
-
-        // Display the current icon bitmap in `edit_bookmark_current_icon`.
-        currentIconImageView.setImageBitmap(currentIconBitmap);
-
-        // Set the new favorite icon bitmap.
-        newFavoriteIconImageView.setImageBitmap(favoriteIconBitmap);
-
-        // Populate the folder name edit text.
-        nameEditText.setText(currentFolderName);
-
-        // Setup a matrix cursor for "Home Folder".
-        String[] matrixCursorColumnNames = {BookmarksDatabaseHelper._ID, BookmarksDatabaseHelper.BOOKMARK_NAME};
-        MatrixCursor matrixCursor = new MatrixCursor(matrixCursorColumnNames);
-        matrixCursor.addRow(new Object[]{BookmarksDatabaseViewActivity.HOME_FOLDER_DATABASE_ID, getString(R.string.home_folder)});
-
-        // Add all subfolders of the current folder to the list of folders not to display.
-        String exceptFolders = getStringOfSubfolders(currentFolderName, bookmarksDatabaseHelper);
-
-        Log.i("Folders", "String of Folders Not To Display:  " + exceptFolders);
-
-        // Get a cursor with the list of all the folders.
-        Cursor foldersCursor = bookmarksDatabaseHelper.getFoldersExcept(exceptFolders);
-
-        // Combine the matrix cursor and the folders cursor.
-        MergeCursor foldersMergeCursor = new MergeCursor(new Cursor[]{matrixCursor, foldersCursor});
-
-        // Remove the incorrect lint warning that `getContext()` might be null.
-        assert getContext() != null;
-
-        // Create a resource cursor adapter for the spinner.
-        ResourceCursorAdapter foldersCursorAdapter = new ResourceCursorAdapter(getContext(), R.layout.databaseview_spinner_item, foldersMergeCursor, 0) {
-            @Override
-            public void bindView(View view, Context context, Cursor cursor) {
-                // Get handles for the spinner views.
-                ImageView spinnerItemImageView = view.findViewById(R.id.spinner_item_imageview);
-                TextView spinnerItemTextView = view.findViewById(R.id.spinner_item_textview);
-
-                // Set the folder icon according to the type.
-                if (foldersMergeCursor.getPosition() == 0) {  // Set the `Home Folder` icon.
-                    // Set the gray folder image.  `ContextCompat` must be used until the minimum API >= 21.
-                    spinnerItemImageView.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.folder_gray));
-                } else {  // Set a user folder icon.
-                    // Get the folder icon byte array.
-                    byte[] folderIconByteArray = cursor.getBlob(cursor.getColumnIndex(BookmarksDatabaseHelper.FAVORITE_ICON));
-
-                    // Convert the byte array to a bitmap beginning at the first byte and ending at the last.
-                    Bitmap folderIconBitmap = BitmapFactory.decodeByteArray(folderIconByteArray, 0, folderIconByteArray.length);
-
-                    // Set the folder icon.
-                    spinnerItemImageView.setImageBitmap(folderIconBitmap);
-                }
-
-                // Set the text view to display the folder name.
-                spinnerItemTextView.setText(cursor.getString(cursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME)));
-            }
-        };
-
-        // Set the `ResourceCursorAdapter` drop drown view resource.
-        foldersCursorAdapter.setDropDownViewResource(R.layout.databaseview_spinner_dropdown_items);
-
-        // Set the adapter for the folder `Spinner`.
-        folderSpinner.setAdapter(foldersCursorAdapter);
-
-        // Select the current folder in the `Spinner` if the bookmark isn't in the "Home Folder".
-        if (!parentFolder.equals("")) {
-            // Get the database ID of the parent folder.
-            int parentFolderDatabaseId = bookmarksDatabaseHelper.getFolderDatabaseId(folderCursor.getString(folderCursor.getColumnIndex(BookmarksDatabaseHelper.PARENT_FOLDER)));
-
-            // Initialize `parentFolderPosition` and the iteration variable.
-            int parentFolderPosition = 0;
-            int i = 0;
-
-            // Find the parent folder position in folders `ResourceCursorAdapter`.
-            do {
-                if (foldersCursorAdapter.getItemId(i) == parentFolderDatabaseId) {
-                    // Store the current position for the parent folder.
-                    parentFolderPosition = i;
-                } else {
-                    // Try the next entry.
-                    i++;
-                }
-                // Stop when the parent folder position is found or all the items in the `ResourceCursorAdapter` have been checked.
-            } while ((parentFolderPosition == 0) && (i < foldersCursorAdapter.getCount()));
-
-            // Select the parent folder in the `Spinner`.
-            folderSpinner.setSelection(parentFolderPosition);
-        }
-
-        // Store the current folder database ID.
-        int currentParentFolderDatabaseId = (int) folderSpinner.getSelectedItemId();
-
-        // Populate the display order `EditText`.
-        displayOrderEditText.setText(String.valueOf(folderCursor.getInt(folderCursor.getColumnIndex(BookmarksDatabaseHelper.DISPLAY_ORDER))));
-
-        // Initially disable the edit button.
-        editButton.setEnabled(false);
-
-        // Update the edit button if the icon selection changes.
-        iconRadioGroup.setOnCheckedChangeListener((group, checkedId) -> {
-            // Update the edit button.
-            updateEditButton(alertDialog, bookmarksDatabaseHelper, currentFolderName, currentParentFolderDatabaseId, currentDisplayOrder);
-        });
-
-        // Update the edit button if the bookmark name changes.
-        nameEditText.addTextChangedListener(new TextWatcher() {
-            @Override
-            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
-                // Do nothing.
-            }
-
-            @Override
-            public void onTextChanged(CharSequence s, int start, int before, int count) {
-                // Do nothing.
-            }
-
-            @Override
-            public void afterTextChanged(Editable s) {
-                // Update the edit button.
-                updateEditButton(alertDialog, bookmarksDatabaseHelper, currentFolderName, currentParentFolderDatabaseId, currentDisplayOrder);
-            }
-        });
-
-        // Update the edit button if the folder changes.
-        folderSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
-            @Override
-            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
-                // Update the edit button.
-                updateEditButton(alertDialog, bookmarksDatabaseHelper, currentFolderName, currentParentFolderDatabaseId, currentDisplayOrder);
-            }
-
-            @Override
-            public void onNothingSelected(AdapterView<?> parent) {
-
-            }
-        });
-
-        // Update the edit button if the display order changes.
-        displayOrderEditText.addTextChangedListener(new TextWatcher() {
-            @Override
-            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
-                // Do nothing.
-            }
-
-            @Override
-            public void onTextChanged(CharSequence s, int start, int before, int count) {
-                // Do nothing.
-            }
-
-            @Override
-            public void afterTextChanged(Editable s) {
-                // Update the edit button.
-                updateEditButton(alertDialog, bookmarksDatabaseHelper, currentFolderName, currentParentFolderDatabaseId, currentDisplayOrder);
-            }
-        });
-
-        // Allow the `enter` key on the keyboard to save the bookmark from the bookmark name `EditText`.
-        nameEditText.setOnKeyListener((View v, int keyCode, KeyEvent event) -> {
-            // Save the bookmark if the event is a key-down on the "enter" button.
-            if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER) && editButton.isEnabled()) {  // The enter key was pressed and the edit button is enabled.
-                // Trigger the `Listener` and return the `DialogFragment` to the parent activity.
-                editBookmarkFolderDatabaseViewListener.onSaveBookmarkFolder(this, folderDatabaseId, favoriteIconBitmap);
-
-                // Manually dismiss `alertDialog`.
-                alertDialog.dismiss();
-
-                // Consume the event.
-                return true;
-            } else {  // If any other key was pressed, or if the edit button is currently disabled, do not consume the event.
-                return false;
-            }
-        });
-
-        // Allow the "enter" key on the keyboard to save the bookmark from the display order `EditText`.
-        displayOrderEditText.setOnKeyListener((View v, int keyCode, KeyEvent event) -> {
-            // Save the bookmark if the event is a key-down on the "enter" button.
-            if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER) && editButton.isEnabled()) {  // The enter key was pressed and the edit button is enabled.
-                // Trigger the `Listener` and return the `DialogFragment` to the parent activity.
-                editBookmarkFolderDatabaseViewListener.onSaveBookmarkFolder(this, folderDatabaseId, favoriteIconBitmap);
-
-                // Manually dismiss the `AlertDialog`.
-                alertDialog.dismiss();
-
-                // Consume the event.
-                return true;
-            } else { // If any other key was pressed, or if the edit button is currently disabled, do not consume the event.
-                return false;
-            }
-        });
-
-        // `onCreateDialog` requires the return of an `AlertDialog`.
-        return alertDialog;
-    }
-
-    private void updateEditButton(AlertDialog alertDialog, BookmarksDatabaseHelper bookmarksDatabaseHelper, String currentFolderName, int currentParentFolderDatabaseId, int currentDisplayOrder) {
-        // Get handles for the views.
-        EditText nameEditText = alertDialog.findViewById(R.id.edit_folder_name_edittext);
-        Spinner folderSpinner = alertDialog.findViewById(R.id.edit_folder_parent_folder_spinner);
-        EditText displayOrderEditText = alertDialog.findViewById(R.id.edit_folder_display_order_edittext);
-        RadioButton currentIconRadioButton = alertDialog.findViewById(R.id.edit_folder_current_icon_radiobutton);
-        Button editButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
-
-        // Remove the incorrect lint warning below that the views might be null.
-        assert nameEditText != null;
-        assert folderSpinner != null;
-        assert displayOrderEditText != null;
-        assert currentIconRadioButton != null;
-
-        // Get the values from the dialog.
-        String newFolderName = nameEditText.getText().toString();
-        int newParentFolderDatabaseId = (int) folderSpinner.getSelectedItemId();
-        String newDisplayOrder = displayOrderEditText.getText().toString();
-
-        // Get a cursor for the new folder name if it exists.
-        Cursor folderExistsCursor = bookmarksDatabaseHelper.getFolder(newFolderName);
-
-        // Is the new folder name empty?
-        boolean folderNameNotEmpty = !newFolderName.isEmpty();
-
-        // Does the folder name already exist?
-        boolean folderNameAlreadyExists = (!newFolderName.equals(currentFolderName) && (folderExistsCursor.getCount() > 0));
-
-        // Has the favorite icon changed?
-        boolean iconChanged = !currentIconRadioButton.isChecked();
-
-        // Has the name been renamed?
-        boolean folderRenamed = (!newFolderName.equals(currentFolderName) && !folderNameAlreadyExists);
-
-        // Has the folder changed?
-        boolean parentFolderChanged = newParentFolderDatabaseId != currentParentFolderDatabaseId;
-
-        // Has the display order changed?
-        boolean displayOrderChanged = !newDisplayOrder.equals(String.valueOf(currentDisplayOrder));
-
-        // Is the display order empty?
-        boolean displayOrderNotEmpty = !newDisplayOrder.isEmpty();
-
-        // Update the enabled status of the edit button.
-        editButton.setEnabled((iconChanged || folderRenamed || parentFolderChanged || displayOrderChanged) && folderNameNotEmpty && displayOrderNotEmpty);
-    }
-
-    private String getStringOfSubfolders(String folderName, BookmarksDatabaseHelper bookmarksDatabaseHelper) {
-        // Get a cursor will all the immediate subfolders.
-        Cursor subfoldersCursor = bookmarksDatabaseHelper.getSubfolders(folderName);
-
-        // Initialize a string builder to track the folders not to display in the spinner and populate it with the current folder.
-        StringBuilder exceptFoldersStringBuilder = new StringBuilder(DatabaseUtils.sqlEscapeString(folderName));
-
-        for (int i = 0; i < subfoldersCursor.getCount(); i++) {
-            // Move the subfolder cursor to the current item.
-            subfoldersCursor.moveToPosition(i);
-
-            // Get the name of the subfolder.
-            String subfolderName = subfoldersCursor.getString(subfoldersCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME));
-
-            // Add a comma to the end of the existing string.
-            exceptFoldersStringBuilder.append(",");
-
-            // Get the folder name and run the task for any subfolders.
-            String subfolderString = getStringOfSubfolders(subfolderName, bookmarksDatabaseHelper);
-
-            // Add the folder name to the string builder.
-            exceptFoldersStringBuilder.append(subfolderString);
-        }
-
-        // Return the string of folders.
-        return exceptFoldersStringBuilder.toString();
-    }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDatabaseViewDialog.kt b/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDatabaseViewDialog.kt
new file mode 100644 (file)
index 0000000..603dff2
--- /dev/null
@@ -0,0 +1,437 @@
+/*
+ * Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+ *
+ * Privacy Browser 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 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.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.stoutner.privacybrowser.dialogs
+
+import android.annotation.SuppressLint
+import android.app.Dialog
+import android.content.Context
+import android.content.DialogInterface
+import android.database.Cursor
+import android.database.DatabaseUtils
+import android.database.MatrixCursor
+import android.database.MergeCursor
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.os.Bundle
+import android.text.Editable
+import android.text.TextWatcher
+import android.view.KeyEvent
+import android.view.View
+import android.view.WindowManager
+import android.widget.*
+import android.widget.AdapterView.OnItemSelectedListener
+
+import androidx.appcompat.app.AlertDialog
+import androidx.core.content.ContextCompat
+import androidx.cursoradapter.widget.ResourceCursorAdapter
+import androidx.fragment.app.DialogFragment
+import androidx.preference.PreferenceManager
+
+import com.stoutner.privacybrowser.R
+import com.stoutner.privacybrowser.activities.BookmarksDatabaseViewActivity
+import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper
+
+import java.io.ByteArrayOutputStream
+
+// Declare the class constants.
+private const val DATABASE_ID = "database_id"
+private const val FAVORITE_ICON_BYTE_ARRAY = "favorite_icon_byte_array"
+
+class EditBookmarkFolderDatabaseViewDialog: DialogFragment() {
+    // The public interface is used to send information back to the parent activity.
+    interface EditBookmarkFolderDatabaseViewListener {
+        fun onSaveBookmarkFolder(dialogFragment: DialogFragment, selectedFolderDatabaseId: Int, favoriteIconBitmap: Bitmap)
+    }
+
+    // Declare the class variables.
+    private lateinit var editBookmarkFolderDatabaseViewListener: EditBookmarkFolderDatabaseViewListener
+
+    // Declare the class views.
+    private lateinit var nameEditText: EditText
+    private lateinit var folderSpinner: Spinner
+    private lateinit var displayOrderEditText: EditText
+    private lateinit var currentIconRadioButton: RadioButton
+    private lateinit var saveButton: Button
+
+    override fun onAttach(context: Context) {
+        // Run the default commands.
+        super.onAttach(context)
+
+        // Get a handle for edit bookmark database view listener from the launching context.
+        editBookmarkFolderDatabaseViewListener = context as EditBookmarkFolderDatabaseViewListener
+    }
+
+    companion object {
+        // `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin.  Also, the function can then be moved out of a companion object and just become a package-level function.
+        @JvmStatic
+        fun folderDatabaseId(databaseId: Int, favoriteIconBitmap: Bitmap): EditBookmarkFolderDatabaseViewDialog {
+            // Create a favorite icon byte array output stream.
+            val favoriteIconByteArrayOutputStream = ByteArrayOutputStream()
+
+            // Convert the favorite icon to a PNG and place it in the byte array output stream.  `0` is for lossless compression (the only option for a PNG).
+            favoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, favoriteIconByteArrayOutputStream)
+
+            // Convert the byte array output stream to a byte array.
+            val favoriteIconByteArray = favoriteIconByteArrayOutputStream.toByteArray()
+
+            // Create an arguments bundle.
+            val argumentsBundle = Bundle()
+
+            // Store the variables in the bundle.
+            argumentsBundle.putInt(DATABASE_ID, databaseId)
+            argumentsBundle.putByteArray(FAVORITE_ICON_BYTE_ARRAY, favoriteIconByteArray)
+
+            // Create a new instance of the dialog.
+            val editBookmarkFolderDatabaseViewDialog = EditBookmarkFolderDatabaseViewDialog()
+
+            // Add the arguments bundle to the dialog.
+            editBookmarkFolderDatabaseViewDialog.arguments = argumentsBundle
+
+            // Return the new dialog.
+            return editBookmarkFolderDatabaseViewDialog
+        }
+    }
+
+    // `@SuppressLint("InflateParams")` removes the warning about using `null` as the parent view group when inflating the alert dialog.
+    @SuppressLint("InflateParams")
+    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+        // Get a handle for the arguments.
+        val arguments = requireArguments()
+
+        // Get the variables from the arguments.
+        val folderDatabaseId = arguments.getInt(DATABASE_ID)
+        val favoriteIconByteArray = arguments.getByteArray(FAVORITE_ICON_BYTE_ARRAY)!!
+
+        // 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)
+
+        // Get a cursor with the selected bookmark.
+        val folderCursor = bookmarksDatabaseHelper.getBookmark(folderDatabaseId)
+
+        // Move the cursor to the first position.
+        folderCursor.moveToFirst()
+
+        // Use an alert dialog builder to create the alert dialog.
+        val dialogBuilder = AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog)
+
+        // Set the title.
+        dialogBuilder.setTitle(R.string.edit_folder)
+
+        // Set the view.  The parent view is `null` because it will be assigned by the alert dialog.
+        dialogBuilder.setView(requireActivity().layoutInflater.inflate(R.layout.edit_bookmark_folder_databaseview_dialog, null))
+
+        // Set the cancel button listener.  Using `null` as the listener closes the dialog without doing anything else.
+        dialogBuilder.setNegativeButton(R.string.cancel, null)
+
+        // Set the save button listener.
+        dialogBuilder.setPositiveButton(R.string.save) { _: DialogInterface?, _: Int ->
+            // Return the dialog fragment to the parent activity.
+            editBookmarkFolderDatabaseViewListener.onSaveBookmarkFolder(this, folderDatabaseId, favoriteIconBitmap)
+        }
+
+        // Create an alert dialog from the alert dialog builder.
+        val alertDialog = dialogBuilder.create()
+
+        // Get a handle for the shared preferences.
+        val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
+
+        // Get the screenshot preference.
+        val allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false)
+
+        // Disable screenshots if not allowed.
+        if (!allowScreenshots) {
+            alertDialog.window!!.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
+        }
+
+        // The alert dialog must be shown before items in the layout can be modified.
+        alertDialog.show()
+
+        // Get handles for the layout items.
+        val databaseIdTextView = alertDialog.findViewById<TextView>(R.id.edit_folder_database_id_textview)!!
+        val iconRadioGroup = alertDialog.findViewById<RadioGroup>(R.id.edit_folder_icon_radiogroup)!!
+        val currentIconImageView = alertDialog.findViewById<ImageView>(R.id.edit_folder_current_icon_imageview)!!
+        val newFavoriteIconImageView = alertDialog.findViewById<ImageView>(R.id.edit_folder_webpage_favorite_icon_imageview)!!
+        currentIconRadioButton = alertDialog.findViewById(R.id.edit_folder_current_icon_radiobutton)!!
+        nameEditText = alertDialog.findViewById(R.id.edit_folder_name_edittext)!!
+        folderSpinner = alertDialog.findViewById(R.id.edit_folder_parent_folder_spinner)!!
+        displayOrderEditText = alertDialog.findViewById(R.id.edit_folder_display_order_edittext)!!
+        saveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE)
+
+        // Store the current folder values.
+        val currentFolderName = folderCursor.getString(folderCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME))
+        val currentDisplayOrder = folderCursor.getInt(folderCursor.getColumnIndex(BookmarksDatabaseHelper.DISPLAY_ORDER))
+        val parentFolder = folderCursor.getString(folderCursor.getColumnIndex(BookmarksDatabaseHelper.PARENT_FOLDER))
+
+        // Populate the database ID text view.
+        databaseIdTextView.text = folderCursor.getInt(folderCursor.getColumnIndex(BookmarksDatabaseHelper._ID)).toString()
+
+        // Get the current favorite icon byte array from the cursor.
+        val currentIconByteArray = folderCursor.getBlob(folderCursor.getColumnIndex(BookmarksDatabaseHelper.FAVORITE_ICON))
+
+        // Convert the byte array to a bitmap beginning at the first byte and ending at the last.
+        val currentIconBitmap = BitmapFactory.decodeByteArray(currentIconByteArray, 0, currentIconByteArray.size)
+
+        // Populate the current icon image view.
+        currentIconImageView.setImageBitmap(currentIconBitmap)
+
+        // Populate the new favorite icon image view.
+        newFavoriteIconImageView.setImageBitmap(favoriteIconBitmap)
+
+        // Populate the folder name edit text.
+        nameEditText.setText(currentFolderName)
+
+        // Define an array of matrix cursor column names.
+        val matrixCursorColumnNames = arrayOf(BookmarksDatabaseHelper._ID, BookmarksDatabaseHelper.BOOKMARK_NAME)
+
+        // Create a matrix cursor.
+        val matrixCursor = MatrixCursor(matrixCursorColumnNames)
+
+        // Add `Home Folder` to the matrix cursor.
+        matrixCursor.addRow(arrayOf(BookmarksDatabaseViewActivity.HOME_FOLDER_DATABASE_ID, getString(R.string.home_folder)))
+
+        // Get a string of the current folder and all subfolders.
+        val currentAndSubfolderString = getStringOfSubfolders(currentFolderName, bookmarksDatabaseHelper)
+
+        // Get a cursor with the list of all the folders.
+        val foldersCursor = bookmarksDatabaseHelper.getFoldersExcept(currentAndSubfolderString)
+
+        // Combine the matrix cursor and the folders cursor.
+        val combinedFoldersCursor = MergeCursor(arrayOf(matrixCursor, foldersCursor))
+
+        // Create a resource cursor adapter for the spinner.
+        val foldersCursorAdapter: ResourceCursorAdapter = object: ResourceCursorAdapter(context, R.layout.databaseview_spinner_item, combinedFoldersCursor, 0) {
+            override fun bindView(view: View, context: Context, cursor: Cursor) {
+                // Get handles for the spinner views.
+                val spinnerItemImageView = view.findViewById<ImageView>(R.id.spinner_item_imageview)
+                val spinnerItemTextView = view.findViewById<TextView>(R.id.spinner_item_textview)
+
+                // Set the folder icon according to the type.
+                if (combinedFoldersCursor.position == 0) {  // Set the `Home Folder` icon.
+                    // Set the gray folder image.  `ContextCompat` must be used until the minimum API >= 21.
+                    spinnerItemImageView.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.folder_gray))
+                } else {  // Set a user folder icon.
+                    // Get the folder icon byte array.
+                    val folderIconByteArray = cursor.getBlob(cursor.getColumnIndex(BookmarksDatabaseHelper.FAVORITE_ICON))
+
+                    // Convert the byte array to a bitmap beginning at the first byte and ending at the last.
+                    val folderIconBitmap = BitmapFactory.decodeByteArray(folderIconByteArray, 0, folderIconByteArray.size)
+
+                    // Set the folder icon.
+                    spinnerItemImageView.setImageBitmap(folderIconBitmap)
+                }
+
+                // Set the text view to display the folder name.
+                spinnerItemTextView.text = cursor.getString(cursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME))
+            }
+        }
+
+        // Set the folders cursor adapter drop drown view resource.
+        foldersCursorAdapter.setDropDownViewResource(R.layout.databaseview_spinner_dropdown_items)
+
+        // Set the adapter for the folder `Spinner`.
+        folderSpinner.adapter = foldersCursorAdapter
+
+        // Select the current folder in the spinner if the bookmark isn't in the "Home Folder".
+        if (parentFolder != "") {
+            // Get the database ID of the parent folder as a long.
+            val parentFolderDatabaseId = bookmarksDatabaseHelper.getFolderDatabaseId(folderCursor.getString(folderCursor.getColumnIndex(BookmarksDatabaseHelper.PARENT_FOLDER))).toLong()
+
+            // Initialize the parent folder position and the iteration variable.
+            var parentFolderPosition = 0
+            var i = 0
+
+            // Find the parent folder position in the folders cursor adapter.
+            do {
+                if (foldersCursorAdapter.getItemId(i) == parentFolderDatabaseId) {
+                    // Store the current position for the parent folder.
+                    parentFolderPosition = i
+                } else {
+                    // Try the next entry.
+                    i++
+                }
+                // Stop when the parent folder position is found or all the items in the folders cursor adapter have been checked.
+            } while (parentFolderPosition == 0 && i < foldersCursorAdapter.count)
+
+            // Select the parent folder in the spinner.
+            folderSpinner.setSelection(parentFolderPosition)
+        }
+
+        // Store the current folder database ID.
+        val currentParentFolderDatabaseId = folderSpinner.selectedItemId.toInt()
+
+        // Populate the display order edit text.
+        displayOrderEditText.setText(folderCursor.getInt(folderCursor.getColumnIndex(BookmarksDatabaseHelper.DISPLAY_ORDER)).toString())
+
+        // Initially disable the edit button.
+        saveButton.isEnabled = false
+
+        // Update the save button if the icon selection changes.
+        iconRadioGroup.setOnCheckedChangeListener { _: RadioGroup?, _: Int ->
+            // Update the save button.
+            updateSaveButton(bookmarksDatabaseHelper, currentFolderName, currentParentFolderDatabaseId, currentDisplayOrder)
+        }
+
+        // Update the save button if the bookmark name changes.
+        nameEditText.addTextChangedListener(object: TextWatcher {
+            override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
+                // Do nothing.
+            }
+
+            override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
+                // Do nothing.
+            }
+
+            override fun afterTextChanged(s: Editable) {
+                // Update the save button.
+                updateSaveButton(bookmarksDatabaseHelper, currentFolderName, currentParentFolderDatabaseId, currentDisplayOrder)
+            }
+        })
+
+        // Update the save button if the folder changes.
+        folderSpinner.onItemSelectedListener = object: OnItemSelectedListener {
+            override fun onItemSelected(parent: AdapterView<*>?, view: View, position: Int, id: Long) {
+                // Update the save button.
+                updateSaveButton(bookmarksDatabaseHelper, currentFolderName, currentParentFolderDatabaseId, currentDisplayOrder)
+            }
+
+            override fun onNothingSelected(parent: AdapterView<*>?) {
+                // Do nothing.
+            }
+        }
+
+        // Update the save button if the display order changes.
+        displayOrderEditText.addTextChangedListener(object: TextWatcher {
+            override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
+                // Do nothing.
+            }
+
+            override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
+                // Do nothing.
+            }
+
+            override fun afterTextChanged(s: Editable) {
+                // Update the save button.
+                updateSaveButton(bookmarksDatabaseHelper, currentFolderName, currentParentFolderDatabaseId, currentDisplayOrder)
+            }
+        })
+
+        // Allow the enter key on the keyboard to save the bookmark from the bookmark name edit text.
+        nameEditText.setOnKeyListener { _: View?, keyCode: Int, event: KeyEvent ->
+            // Check the key code, event, and button status.
+            if (event.action == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER && saveButton.isEnabled) {  // The enter key was pressed and the save button is enabled.
+                // Trigger the listener and return the dialog fragment to the parent activity.
+                editBookmarkFolderDatabaseViewListener.onSaveBookmarkFolder(this, folderDatabaseId, favoriteIconBitmap)
+
+                // Manually dismiss the alert dialog.
+                alertDialog.dismiss()
+
+                // Consume the event.
+                return@setOnKeyListener true
+            } else {  // If any other key was pressed, or if the save button is currently disabled, do not consume the event.
+                return@setOnKeyListener false
+            }
+        }
+
+        // Allow the enter key on the keyboard to save the bookmark from the display order edit text.
+        displayOrderEditText.setOnKeyListener { _: View?, keyCode: Int, event: KeyEvent ->
+            // Check the key code, event, and button status.
+            if (event.action == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER && saveButton.isEnabled) {  // The enter key was pressed and the save button is enabled.
+                // Trigger the listener and return the dialog fragment to the parent activity.
+                editBookmarkFolderDatabaseViewListener.onSaveBookmarkFolder(this, folderDatabaseId, favoriteIconBitmap)
+
+                // Manually dismiss the alert dialog.
+                alertDialog.dismiss()
+
+                // Consume the event.
+                return@setOnKeyListener true
+            } else { // If any other key was pressed, or if the save button is currently disabled, do not consume the event.
+                return@setOnKeyListener false
+            }
+        }
+
+        // Return the alert dialog.
+        return alertDialog
+    }
+
+    private fun updateSaveButton(bookmarksDatabaseHelper: BookmarksDatabaseHelper, currentFolderName: String, currentParentFolderDatabaseId: Int, currentDisplayOrder: Int) {
+        // Get the values from the views.
+        val newFolderName = nameEditText.text.toString()
+        val newParentFolderDatabaseId = folderSpinner.selectedItemId.toInt()
+        val newDisplayOrder = displayOrderEditText.text.toString()
+
+        // Get a cursor for the new folder name if it exists.
+        val folderExistsCursor = bookmarksDatabaseHelper.getFolder(newFolderName)
+
+        // Is the new folder name empty?
+        val folderNameNotEmpty = newFolderName.isNotEmpty()
+
+        // Does the folder name already exist?
+        val folderNameAlreadyExists = (newFolderName != currentFolderName) && folderExistsCursor.count > 0
+
+        // Has the favorite icon changed?
+        val iconChanged = !currentIconRadioButton.isChecked
+
+        // Has the folder been renamed?
+        val folderRenamed = (newFolderName != currentFolderName) && !folderNameAlreadyExists
+
+        // Has the parent folder changed?
+        val parentFolderChanged = newParentFolderDatabaseId != currentParentFolderDatabaseId
+
+        // Has the display order changed?
+        val displayOrderChanged = newDisplayOrder != currentDisplayOrder.toString()
+
+        // Is the display order empty?
+        val displayOrderNotEmpty = newDisplayOrder.isNotEmpty()
+
+        // Update the enabled status of the edit button.
+        saveButton.isEnabled = (iconChanged || folderRenamed || parentFolderChanged || displayOrderChanged) && folderNameNotEmpty && displayOrderNotEmpty
+    }
+
+    private fun getStringOfSubfolders(folderName: String, bookmarksDatabaseHelper: BookmarksDatabaseHelper): String {
+        // Get a cursor will all the immediate subfolders.
+        val subfoldersCursor = bookmarksDatabaseHelper.getSubfolders(folderName)
+
+        // Initialize the subfolder string builder and populate it with the current folder.
+        val currentAndSubfolderStringBuilder = StringBuilder(DatabaseUtils.sqlEscapeString(folderName))
+
+        // Populate the subfolder string builder
+        for (i in 0 until subfoldersCursor.count) {
+            // Move the subfolder cursor to the current item.
+            subfoldersCursor.moveToPosition(i)
+
+            // Get the name of the subfolder.
+            val subfolderName = subfoldersCursor.getString(subfoldersCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME))
+
+            // Add a comma to the end of the existing string.
+            currentAndSubfolderStringBuilder.append(",")
+
+            // Get the folder name and run the task for any subfolders.
+            val subfolderString = getStringOfSubfolders(subfolderName, bookmarksDatabaseHelper)
+
+            // Add the folder name to the string builder.
+            currentAndSubfolderStringBuilder.append(subfolderString)
+        }
+
+        // Return the string of folders.
+        return currentAndSubfolderStringBuilder.toString()
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDialog.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDialog.java
deleted file mode 100644 (file)
index 216f0d3..0000000
+++ /dev/null
@@ -1,287 +0,0 @@
-/*
- * Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
- *
- * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
- *
- * Privacy Browser 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 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.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.stoutner.privacybrowser.dialogs;
-
-import android.annotation.SuppressLint;
-import android.app.Dialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.SharedPreferences;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.os.Bundle;
-import android.preference.PreferenceManager;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.ImageView;
-import android.widget.RadioButton;
-import android.widget.RadioGroup;
-
-import androidx.annotation.NonNull;
-import androidx.appcompat.app.AlertDialog;
-import androidx.fragment.app.DialogFragment;  // The AndroidX dialog fragment must be used or an error is produced on API <=22.
-
-import com.stoutner.privacybrowser.R;
-import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper;
-
-import java.io.ByteArrayOutputStream;
-
-public class EditBookmarkFolderDialog extends DialogFragment {
-    // Instantiate the class variable.
-    private EditBookmarkFolderListener editBookmarkFolderListener;
-
-    // The public interface is used to send information back to the parent activity.
-    public interface EditBookmarkFolderListener {
-        void onSaveBookmarkFolder(DialogFragment dialogFragment, int selectedFolderDatabaseId, Bitmap favoriteIconBitmap);
-    }
-
-    public void onAttach(@NonNull Context context) {
-        // Run the default commands.
-        super.onAttach(context);
-
-        // Get a handle for `EditFolderListener` from the launching context.
-        editBookmarkFolderListener = (EditBookmarkFolderListener) context;
-    }
-
-    // Store the database ID in the arguments bundle.
-    public static EditBookmarkFolderDialog folderDatabaseId(int databaseId, Bitmap favoriteIconBitmap) {
-        // Create a favorite icon byte array output stream.
-        ByteArrayOutputStream favoriteIconByteArrayOutputStream = new ByteArrayOutputStream();
-
-        // Convert the favorite icon to a PNG and place it in the byte array output stream.  `0` is for lossless compression (the only option for a PNG).
-        favoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, favoriteIconByteArrayOutputStream);
-
-        // Convert the byte array output stream to a byte array.
-        byte[] favoriteIconByteArray = favoriteIconByteArrayOutputStream.toByteArray();
-
-        // Create an arguments bundle
-        Bundle argumentsBundle = new Bundle();
-
-        // Store the variables in the bundle.
-        argumentsBundle.putInt("database_id", databaseId);
-        argumentsBundle.putByteArray("favorite_icon_byte_array", favoriteIconByteArray);
-
-        // Create a new instance of the dialog.
-        EditBookmarkFolderDialog editBookmarkFolderDialog = new EditBookmarkFolderDialog();
-
-        // Add the arguments bundle to the dialog.
-        editBookmarkFolderDialog.setArguments(argumentsBundle);
-
-        // Return the new dialog.
-        return editBookmarkFolderDialog;
-    }
-
-    // `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`.
-    @SuppressLint("InflateParams")
-    @Override
-    @NonNull
-    public Dialog onCreateDialog(Bundle savedInstanceState) {
-        // Get the arguments.
-        Bundle arguments = getArguments();
-
-        // Remove the incorrect lint warning below that the arguments might be null.
-        assert arguments != null;
-
-        // Store the folder database ID in the class variable.
-        int selectedFolderDatabaseId = arguments.getInt("database_id");
-
-        // Get the favorite icon byte array.
-        byte[] favoriteIconByteArray = arguments.getByteArray("favorite_icon_byte_array");
-
-        // Remove the incorrect lint warning below that the favorite icon byte array might be null.
-        assert favoriteIconByteArray != null;
-
-        // Convert the favorite icon byte array to a bitmap.
-        Bitmap favoriteIconBitmap = BitmapFactory.decodeByteArray(favoriteIconByteArray, 0, favoriteIconByteArray.length);
-
-        // Initialize the database helper.  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 bookmarksDatabaseHelper = new BookmarksDatabaseHelper(getContext(), null, null, 0);
-
-        // Get a cursor with the selected folder and move it to the first position.
-        Cursor folderCursor = bookmarksDatabaseHelper.getBookmark(selectedFolderDatabaseId);
-        folderCursor.moveToFirst();
-
-        // Use an alert dialog builder to create the alert dialog.
-        AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog);
-
-        // Set the title.
-        dialogBuilder.setTitle(R.string.edit_folder);
-
-        // Remove the incorrect lint warning that `getActivity()` might be null.
-        assert getActivity() != null;
-
-        // Set the view.  The parent view is `null` because it will be assigned by `AlertDialog`.
-        dialogBuilder.setView(getActivity().getLayoutInflater().inflate(R.layout.edit_bookmark_folder_dialog, null));
-
-        // Set the listener for the negative button.
-        dialogBuilder.setNegativeButton(R.string.cancel, (DialogInterface dialog, int which) -> {
-            // Do nothing.  The `AlertDialog` will close automatically.
-        });
-
-        // Set the listener fo the positive button.
-        dialogBuilder.setPositiveButton(R.string.save, (DialogInterface dialog, int which) -> {
-            // Return the `DialogFragment` to the parent activity on save.
-            editBookmarkFolderListener.onSaveBookmarkFolder(this, selectedFolderDatabaseId, favoriteIconBitmap);
-        });
-
-        // Create an alert dialog from the alert dialog builder.
-        AlertDialog alertDialog = dialogBuilder.create();
-
-        // Remove the warning below that `getWindow()` might be null.
-        assert alertDialog.getWindow() != null;
-
-        // Get a handle for the shared preferences.
-        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());
-
-        // Get the screenshot preference.
-        boolean allowScreenshots = sharedPreferences.getBoolean("allow_screenshots", false);
-
-        // Disable screenshots if not allowed.
-        if (!allowScreenshots) {
-            alertDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
-        }
-
-        // The alert dialog must be shown before items in the layout can be modified.
-        alertDialog.show();
-
-        // Get handles for the views in the alert dialog.
-        RadioGroup iconRadioGroup = alertDialog.findViewById(R.id.edit_folder_icon_radio_group);
-        RadioButton currentIconRadioButton = alertDialog.findViewById(R.id.edit_folder_current_icon_radiobutton);
-        ImageView currentIconImageView = alertDialog.findViewById(R.id.edit_folder_current_icon_imageview);
-        ImageView webPageFavoriteIconImageView = alertDialog.findViewById(R.id.edit_folder_web_page_favorite_icon_imageview);
-        EditText folderNameEditText = alertDialog.findViewById(R.id.edit_folder_name_edittext);
-        Button editButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
-
-        // Remove the incorrect lint warnings below that the views might be null.
-        assert iconRadioGroup != null;
-        assert currentIconRadioButton != null;
-        assert currentIconImageView != null;
-        assert webPageFavoriteIconImageView != null;
-        assert folderNameEditText != null;
-
-        // Initially disable the edit button.
-        editButton.setEnabled(false);
-
-        // Get the current favorite icon byte array from the Cursor.
-        byte[] currentIconByteArray = folderCursor.getBlob(folderCursor.getColumnIndex(BookmarksDatabaseHelper.FAVORITE_ICON));
-
-        // Convert the byte array to a bitmap beginning at the first byte and ending at the last.
-        Bitmap currentIconBitmap = BitmapFactory.decodeByteArray(currentIconByteArray, 0, currentIconByteArray.length);
-
-        // Display the current icon bitmap.
-        currentIconImageView.setImageBitmap(currentIconBitmap);
-
-        // Set the new favorite icon bitmap.
-        webPageFavoriteIconImageView.setImageBitmap(favoriteIconBitmap);
-
-        // Get the current folder name.
-        String currentFolderName = folderCursor.getString(folderCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME));
-
-        // Display the current folder name in `edit_folder_name_edittext`.
-        folderNameEditText.setText(currentFolderName);
-
-        // Update the status of the edit button when the folder name is changed.
-        folderNameEditText.addTextChangedListener(new TextWatcher() {
-            @Override
-            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
-                // Do nothing.
-            }
-
-            @Override
-            public void onTextChanged(CharSequence s, int start, int before, int count) {
-                // Do nothing.
-            }
-
-            @Override
-            public void afterTextChanged(Editable s) {
-                // Convert the current text to a string.
-                String newFolderName = s.toString();
-
-                // Get a cursor for the new folder name if it exists.
-                Cursor folderExistsCursor = bookmarksDatabaseHelper.getFolder(newFolderName);
-
-                // Is the new folder name empty?
-                boolean folderNameNotEmpty = !newFolderName.isEmpty();
-
-                // Does the folder name already exist?
-                boolean folderNameAlreadyExists = (!newFolderName.equals(currentFolderName) && (folderExistsCursor.getCount() > 0));
-
-                // Has the folder been renamed?
-                boolean folderRenamed = (!newFolderName.equals(currentFolderName) && !folderNameAlreadyExists);
-
-                // Has the favorite icon changed?
-                boolean iconChanged = (!currentIconRadioButton.isChecked() && !folderNameAlreadyExists);
-
-                // Enable the create button if something has been edited and the new folder name is valid.
-                editButton.setEnabled(folderNameNotEmpty && (folderRenamed || iconChanged));
-            }
-        });
-
-        // Update the status of the edit button when the icon is changed.
-        iconRadioGroup.setOnCheckedChangeListener((RadioGroup group, int checkedId) -> {
-            // Get the new folder name.
-            String newFolderName = folderNameEditText.getText().toString();
-
-            // Get a cursor for the new folder name if it exists.
-            Cursor folderExistsCursor = bookmarksDatabaseHelper.getFolder(newFolderName);
-
-            // Is the new folder name empty?
-            boolean folderNameEmpty = newFolderName.isEmpty();
-
-            // Does the folder name already exist?
-            boolean folderNameAlreadyExists = (!newFolderName.equals(currentFolderName) && (folderExistsCursor.getCount() > 0));
-
-            // Has the folder been renamed?
-            boolean folderRenamed = (!newFolderName.equals(currentFolderName) && !folderNameAlreadyExists);
-
-            // Has the favorite icon changed?
-            boolean iconChanged = (!currentIconRadioButton.isChecked() && !folderNameAlreadyExists);
-
-            // Enable the create button if something has been edited and the new folder name is valid.
-            editButton.setEnabled(!folderNameEmpty && (folderRenamed || iconChanged));
-        });
-
-        // Allow the `enter` key on the keyboard to save the bookmark from `edit_bookmark_name_edittext`.
-        folderNameEditText.setOnKeyListener((View v, int keyCode, KeyEvent event) -> {
-            // If the event is a key-down on the "enter" button, select the PositiveButton `Save`.
-            if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER) && editButton.isEnabled()) {  // The enter key was pressed and the edit button is enabled.
-                // Trigger `editBookmarkListener` and return the DialogFragment to the parent activity.
-                editBookmarkFolderListener.onSaveBookmarkFolder(this, selectedFolderDatabaseId, favoriteIconBitmap);
-
-                // Manually dismiss the `AlertDialog`.
-                alertDialog.dismiss();
-
-                // Consume the event.
-                return true;
-            } else {  // If any other key was pressed, or if the edit button is currently disabled, do not consume the event.
-                return false;
-            }
-        });
-
-        // `onCreateDialog` requires the return of an `AlertDialog`.
-        return alertDialog;
-    }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDialog.kt b/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDialog.kt
new file mode 100644 (file)
index 0000000..5ff29c5
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+ * Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+ *
+ * Privacy Browser 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 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.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.stoutner.privacybrowser.dialogs
+
+import android.annotation.SuppressLint
+import android.app.Dialog
+import android.content.Context
+import android.content.DialogInterface
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.os.Bundle
+import android.text.Editable
+import android.text.TextWatcher
+import android.view.KeyEvent
+import android.view.View
+import android.view.WindowManager
+import android.widget.Button
+import android.widget.EditText
+import android.widget.ImageView
+import android.widget.RadioButton
+import android.widget.RadioGroup
+
+import androidx.appcompat.app.AlertDialog
+import androidx.fragment.app.DialogFragment
+import androidx.preference.PreferenceManager
+
+import com.stoutner.privacybrowser.R
+import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper
+
+import java.io.ByteArrayOutputStream
+
+// Declare the class constants.
+private const val DATABASE_ID = "database_id"
+private const val FAVORITE_ICON_BYTE_ARRAY = "favorite_icon_byte_array"
+
+class EditBookmarkFolderDialog: DialogFragment() {
+    // The public interface is used to send information back to the parent activity.
+    interface EditBookmarkFolderListener {
+        fun onSaveBookmarkFolder(dialogFragment: DialogFragment?, selectedFolderDatabaseId: Int, favoriteIconBitmap: Bitmap?)
+    }
+
+    // Declare the class variables.
+    private lateinit var editBookmarkFolderListener: EditBookmarkFolderListener
+    private lateinit var bookmarksDatabaseHelper: BookmarksDatabaseHelper
+    private lateinit var currentFolderName: String
+
+    // Declare the class views.
+    private lateinit var currentIconRadioButton: RadioButton
+    private lateinit var folderNameEditText: EditText
+    private lateinit var saveButton: Button
+
+    override fun onAttach(context: Context) {
+        // Run the default commands.
+        super.onAttach(context)
+
+        // Get a handle for the edit bookmark folder listener from the launching context.
+        editBookmarkFolderListener = context as EditBookmarkFolderListener
+    }
+
+    companion object {
+        // `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin.  Also, the function can then be moved out of a companion object and just become a package-level function.
+        @JvmStatic
+        fun folderDatabaseId(databaseId: Int, favoriteIconBitmap: Bitmap): EditBookmarkFolderDialog {
+            // Create a favorite icon byte array output stream.
+            val favoriteIconByteArrayOutputStream = ByteArrayOutputStream()
+
+            // Convert the favorite icon to a PNG and place it in the byte array output stream.  `0` is for lossless compression (the only option for a PNG).
+            favoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, favoriteIconByteArrayOutputStream)
+
+            // Convert the byte array output stream to a byte array.
+            val favoriteIconByteArray = favoriteIconByteArrayOutputStream.toByteArray()
+
+            // Create an arguments bundle
+            val argumentsBundle = Bundle()
+
+            // Store the variables in the bundle.
+            argumentsBundle.putInt(DATABASE_ID, databaseId)
+            argumentsBundle.putByteArray(FAVORITE_ICON_BYTE_ARRAY, favoriteIconByteArray)
+
+            // Create a new instance of the dialog.
+            val editBookmarkFolderDialog = EditBookmarkFolderDialog()
+
+            // Add the arguments bundle to the dialog.
+            editBookmarkFolderDialog.arguments = argumentsBundle
+
+            // Return the new dialog.
+            return editBookmarkFolderDialog
+        }
+    }
+
+    // `@SuppressLint("InflateParams")` removes the warning about using `null` as the parent view group when inflating the alert dialog.
+    @SuppressLint("InflateParams")
+    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+        // Get a handle for the arguments.
+        val arguments = requireArguments()
+
+        // Get the variables from the arguments.
+        val selectedFolderDatabaseId = arguments.getInt(DATABASE_ID)
+        val favoriteIconByteArray = arguments.getByteArray(FAVORITE_ICON_BYTE_ARRAY)!!
+
+        // 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)
+
+        // Get a cursor with the selected folder.
+        val folderCursor = bookmarksDatabaseHelper.getBookmark(selectedFolderDatabaseId)
+
+        // Move the cursor to the first position.
+        folderCursor.moveToFirst()
+
+        // Use an alert dialog builder to create the alert dialog.
+        val dialogBuilder = AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog)
+
+        // Set the title.
+        dialogBuilder.setTitle(R.string.edit_folder)
+
+        // Set the view.  The parent view is `null` because it will be assigned by `AlertDialog`.
+        dialogBuilder.setView(requireActivity().layoutInflater.inflate(R.layout.edit_bookmark_folder_dialog, null))
+
+        // Set the cancel button listener.  Using `null` as the listener closes the dialog without doing anything else.
+        dialogBuilder.setNegativeButton(R.string.cancel, null)
+
+        // Set the save button listener.
+        dialogBuilder.setPositiveButton(R.string.save) { _: DialogInterface?, _: Int ->
+            // Return the dialog fragment to the parent activity on save.
+            editBookmarkFolderListener.onSaveBookmarkFolder(this, selectedFolderDatabaseId, favoriteIconBitmap)
+        }
+
+        // Create an alert dialog from the alert dialog builder.
+        val alertDialog = dialogBuilder.create()
+
+        // Get a handle for the shared preferences.
+        val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
+
+        // Get the screenshot preference.
+        val allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false)
+
+        // Disable screenshots if not allowed.
+        if (!allowScreenshots) {
+            alertDialog.window!!.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
+        }
+
+        // The alert dialog must be shown before items in the layout can be modified.
+        alertDialog.show()
+
+        // Get handles for the views in the alert dialog.
+        val iconRadioGroup = alertDialog.findViewById<RadioGroup>(R.id.edit_folder_icon_radio_group)!!
+        currentIconRadioButton = alertDialog.findViewById(R.id.edit_folder_current_icon_radiobutton)!!
+        val currentIconImageView = alertDialog.findViewById<ImageView>(R.id.edit_folder_current_icon_imageview)!!
+        val webPageFavoriteIconImageView = alertDialog.findViewById<ImageView>(R.id.edit_folder_web_page_favorite_icon_imageview)!!
+        folderNameEditText = alertDialog.findViewById(R.id.edit_folder_name_edittext)!!
+        saveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE)
+
+        // Get the current favorite icon byte array from the cursor.
+        val currentIconByteArray = folderCursor.getBlob(folderCursor.getColumnIndex(BookmarksDatabaseHelper.FAVORITE_ICON))
+
+        // Convert the byte array to a bitmap beginning at the first byte and ending at the last.
+        val currentIconBitmap = BitmapFactory.decodeByteArray(currentIconByteArray, 0, currentIconByteArray.size)
+
+        // Display the current icon bitmap.
+        currentIconImageView.setImageBitmap(currentIconBitmap)
+
+        // Set the new favorite icon bitmap.
+        webPageFavoriteIconImageView.setImageBitmap(favoriteIconBitmap)
+
+        // Get the current folder name.
+        currentFolderName = folderCursor.getString(folderCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME))
+
+        // Display the current folder name.
+        folderNameEditText.setText(currentFolderName)
+
+        // Initially disable the save button.
+        saveButton.isEnabled = false
+
+        // Update the status of the save button when the folder name is changed.
+        folderNameEditText.addTextChangedListener(object: TextWatcher {
+            override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
+                // Do nothing.
+            }
+
+            override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
+                // Do nothing.
+            }
+
+            override fun afterTextChanged(s: Editable) {
+                // Update the save button.
+                updateSaveButton()
+            }
+        })
+
+        // Update the status of the save button when the icon is changed.
+        iconRadioGroup.setOnCheckedChangeListener { _: RadioGroup?, _: Int ->
+            // Update the save button.
+            updateSaveButton()
+        }
+
+        // Allow the enter key on the keyboard to save the bookmark from the edit bookmark name edit text.
+        folderNameEditText.setOnKeyListener { _: View?, keyCode: Int, event: KeyEvent ->
+            // Check the key code, event, and button status.
+            if (event.action == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER && saveButton.isEnabled) {  // The enter key was pressed and the save button is enabled.
+                // Trigger the listener and return the dialog fragment to the parent activity.
+                editBookmarkFolderListener.onSaveBookmarkFolder(this, selectedFolderDatabaseId, favoriteIconBitmap)
+
+                // Manually dismiss the the alert dialog.
+                alertDialog.dismiss()
+
+                // Consume the event.
+                return@setOnKeyListener true
+            } else {  // If any other key was pressed, or if the save button is currently disabled, do not consume the event.
+                return@setOnKeyListener false
+            }
+        }
+
+        // Return the alert dialog.
+        return alertDialog
+    }
+
+    private fun updateSaveButton() {
+        // Get the new folder name.
+        val newFolderName = folderNameEditText.text.toString()
+
+        // Get a cursor for the new folder name if it exists.
+        val folderExistsCursor = bookmarksDatabaseHelper.getFolder(newFolderName)
+
+        // Is the new folder name empty?
+        val folderNameEmpty = newFolderName.isEmpty()
+
+        // Does the folder name already exist?
+        val folderNameAlreadyExists = (newFolderName != currentFolderName) && folderExistsCursor.count > 0
+
+        // Has the folder been renamed?
+        val folderRenamed = (newFolderName != currentFolderName) && !folderNameAlreadyExists
+
+        // Has the favorite icon changed?
+        val iconChanged = !currentIconRadioButton.isChecked && !folderNameAlreadyExists
+
+        // Enable the save button if something has been edited and the new folder name is valid.
+        saveButton.isEnabled = !folderNameEmpty && (folderRenamed || iconChanged)
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/FontSizeDialog.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/FontSizeDialog.java
deleted file mode 100644 (file)
index 7a0e07e..0000000
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright © 2019-2020 Soren Stoutner <soren@stoutner.com>.
- *
- * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
- *
- * Privacy Browser 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 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.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.stoutner.privacybrowser.dialogs;
-
-import android.annotation.SuppressLint;
-import android.app.Activity;
-import android.app.Dialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.SharedPreferences;
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.EditText;
-
-import androidx.annotation.NonNull;
-import androidx.appcompat.app.AlertDialog;
-import androidx.fragment.app.DialogFragment;
-import androidx.preference.PreferenceManager;
-
-import com.stoutner.privacybrowser.R;
-
-public class FontSizeDialog extends DialogFragment {
-    // Define the update font size listener.
-    private UpdateFontSizeListener updateFontSizeListener;
-
-    // The public interface is used to send information back to the parent activity.
-    public interface UpdateFontSizeListener {
-        void onApplyNewFontSize(DialogFragment dialogFragment);
-    }
-
-    @Override
-    public void onAttach(@NonNull Context context) {
-        // Run the default commands.
-        super.onAttach(context);
-
-        // Get a handle for the update font size listener from the launching context.
-        updateFontSizeListener = (UpdateFontSizeListener) context;
-    }
-
-    public static FontSizeDialog displayDialog(int fontSize) {
-        // Create an arguments bundle.
-        Bundle argumentsBundle = new Bundle();
-
-        // Store the font size in the bundle.
-        argumentsBundle.putInt("font_size", fontSize);
-
-        // Create a new instance of the dialog.
-        FontSizeDialog fontSizeDialog = new FontSizeDialog();
-
-        // Add the bundle to the dialog.
-        fontSizeDialog.setArguments(argumentsBundle);
-
-        // Return the new dialog.
-        return fontSizeDialog;
-    }
-
-    // `@SuppressLing("InflateParams")` removes the warning about using null as the parent view group when inflating the alert dialog.
-    @SuppressLint("InflateParams")
-    @Override
-    @NonNull
-    public Dialog onCreateDialog(Bundle savedInstanceState) {
-        // Get a handle for the activity and the context.
-        Activity activity = getActivity();
-        Context context = getContext();
-
-        // Remove the incorrect lint warnings below that the activity and context might be null.
-        assert activity != null;
-        assert context != null;
-
-        // Get the arguments.
-        Bundle arguments = getArguments();
-
-        // Remove the incorrect lint warning below that `getInt()` might be null.
-        assert arguments != null;
-
-        // Get the current font size.
-        int currentFontSize = arguments.getInt("font_size");
-
-        // Use a builder to create the alert dialog.
-        AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context, R.style.PrivacyBrowserAlertDialog);
-
-        // Get the current theme status.
-        int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
-
-        // Set the icon according to the theme.
-        if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
-            dialogBuilder.setIcon(R.drawable.font_size_night);
-        } else {
-            dialogBuilder.setIcon(R.drawable.font_size_day);
-        }
-
-        // Set the title.
-        dialogBuilder.setTitle(R.string.font_size);
-
-        // Set the view.  The parent view is null because it will be assigned by the alert dialog.
-        dialogBuilder.setView(activity.getLayoutInflater().inflate(R.layout.font_size_dialog, null));
-
-        // Set the close button listener.  Using `null` as the listener closes the dialog without doing anything else.
-        dialogBuilder.setNegativeButton(R.string.close, null);
-
-        // Set the apply button listener.
-        dialogBuilder.setPositiveButton(R.string.apply, (DialogInterface dialog, int which) -> {
-            // Return the dialog fragment to the parent activity.
-            updateFontSizeListener.onApplyNewFontSize(this);
-        });
-
-        // Create an alert dialog from the builder.
-        AlertDialog alertDialog = dialogBuilder.create();
-
-        // Get the alert dialog window.
-        Window dialogWindow = alertDialog.getWindow();
-
-        // Remove the incorrect lint warning below that the dialog window might be null.
-        assert dialogWindow != null;
-
-        // Get a handle for the shared preferences.
-        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
-
-        // Get the screenshot preferences.
-        boolean allowScreenshots = sharedPreferences.getBoolean("allow_screenshots", false);
-
-        // Disable screenshots if not allowed.
-        if (!allowScreenshots) {
-            dialogWindow.addFlags(WindowManager.LayoutParams.FLAG_SECURE);
-        }
-
-        // Display the keyboard.
-        dialogWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
-
-        // The alert dialog must be shown before items in the layout can be modified.
-        alertDialog.show();
-
-        // Get a handle for the font size edit text.
-        EditText fontSizeEditText = alertDialog.findViewById(R.id.font_size_edittext);
-
-        // Remove the incorrect lint warning below that the edit text might be null.
-        assert fontSizeEditText != null;
-
-        // Display the current font size.
-        fontSizeEditText.setText(String.valueOf(currentFontSize));
-
-        // Request focus on the font size edit text.
-        fontSizeEditText.requestFocus();
-
-        // Set the enter key on the keyboard to update the font size.
-        fontSizeEditText.setOnKeyListener((View view, int keyCode, KeyEvent keyEvent) -> {
-            // If the key event is a key-down on the `enter` key apply the new font size.
-            if ((keyEvent.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {  // The enter key was pressed.
-                // Trigger the update font size listener and return the dialog fragment to the parent activity.
-                updateFontSizeListener.onApplyNewFontSize((this));
-
-                // Manually dismiss the alert dialog.
-                alertDialog.dismiss();
-
-                //Consume the event.
-                return true;
-            } else {  // If any other key was pressed do not consume the event.
-                return false;
-            }
-        });
-
-        // Return the alert dialog.
-        return alertDialog;
-    }
-}
diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/FontSizeDialog.kt b/app/src/main/java/com/stoutner/privacybrowser/dialogs/FontSizeDialog.kt
new file mode 100644 (file)
index 0000000..75ef4e4
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * Copyright © 2019-2020 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+ *
+ * Privacy Browser 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 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.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.stoutner.privacybrowser.dialogs
+
+import android.annotation.SuppressLint
+import android.app.Dialog
+import android.content.Context
+import android.content.DialogInterface
+import android.content.res.Configuration
+import android.os.Bundle
+import android.view.KeyEvent
+import android.view.View
+import android.view.WindowManager
+import android.widget.EditText
+
+import androidx.appcompat.app.AlertDialog
+import androidx.fragment.app.DialogFragment
+import androidx.preference.PreferenceManager
+
+import com.stoutner.privacybrowser.R
+
+// Declare the class constants.
+private const val FONT_SIZE = "font_size"
+
+class FontSizeDialog: DialogFragment() {
+    // The public interface is used to send information back to the parent activity.
+    interface UpdateFontSizeListener {
+        fun onApplyNewFontSize(dialogFragment: DialogFragment?)
+    }
+
+    // Declare the class variables.
+    private lateinit var updateFontSizeListener: UpdateFontSizeListener
+
+    override fun onAttach(context: Context) {
+        // Run the default commands.
+        super.onAttach(context)
+
+        // Get a handle for the update font size listener from the launching context.
+        updateFontSizeListener = context as UpdateFontSizeListener
+    }
+
+    companion object {
+        // `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin.  Also, the function can then be moved out of a companion object and just become a package-level function.
+        @JvmStatic
+        fun displayDialog(fontSize: Int): FontSizeDialog {
+            // Create an arguments bundle.
+            val argumentsBundle = Bundle()
+
+            // Store the font size in the bundle.
+            argumentsBundle.putInt(FONT_SIZE, fontSize)
+
+            // Create a new instance of the dialog.
+            val fontSizeDialog = FontSizeDialog()
+
+            // Add the bundle to the dialog.
+            fontSizeDialog.arguments = argumentsBundle
+
+            // Return the new dialog.
+            return fontSizeDialog
+        }
+    }
+
+    // `@SuppressLint("InflateParams")` removes the warning about using null as the parent view group when inflating the alert dialog.
+    @SuppressLint("InflateParams")
+    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+        // Get the current font size from the arguments.
+        val currentFontSize = requireArguments().getInt(FONT_SIZE)
+
+        // Use a builder to create the alert dialog.
+        val dialogBuilder = AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog)
+
+        // Get the current theme status.
+        val currentThemeStatus = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
+
+        // Set the icon according to the theme.
+        if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+            dialogBuilder.setIcon(R.drawable.font_size_day)
+        } else {
+            dialogBuilder.setIcon(R.drawable.font_size_night)
+        }
+
+        // Set the title.
+        dialogBuilder.setTitle(R.string.font_size)
+
+        // Set the view.  The parent view is null because it will be assigned by the alert dialog.
+        dialogBuilder.setView(requireActivity().layoutInflater.inflate(R.layout.font_size_dialog, null))
+
+        // Set the close button listener.  Using `null` as the listener closes the dialog without doing anything else.
+        dialogBuilder.setNegativeButton(R.string.close, null)
+
+        // Set the apply button listener.
+        dialogBuilder.setPositiveButton(R.string.apply) { _: DialogInterface?, _: Int ->
+            // Return the dialog fragment to the parent activity.
+            updateFontSizeListener.onApplyNewFontSize(this)
+        }
+
+        // Create an alert dialog from the builder.
+        val alertDialog = dialogBuilder.create()
+
+        // Get the alert dialog window.
+        val dialogWindow = alertDialog.window!!
+
+        // Get a handle for the shared preferences.
+        val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
+
+        // Get the screenshot preferences.
+        val allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false)
+
+        // Disable screenshots if not allowed.
+        if (!allowScreenshots) {
+            dialogWindow.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
+        }
+
+        // Display the keyboard.
+        dialogWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)
+
+        // The alert dialog must be shown before items in the layout can be modified.
+        alertDialog.show()
+
+        // Get a handle for the font size edit text.
+        val fontSizeEditText = alertDialog.findViewById<EditText>(R.id.font_size_edittext)!!
+
+        // Display the current font size.
+        fontSizeEditText.setText(currentFontSize.toString())
+
+        // Request focus on the font size edit text.
+        fontSizeEditText.requestFocus()
+
+        // Set the enter key on the keyboard to update the font size.
+        fontSizeEditText.setOnKeyListener { _: View?, keyCode: Int, keyEvent: KeyEvent ->
+            // Check the key code, event, and button status.
+            if (keyEvent.action == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER) {  // The enter key was pressed.
+                // Trigger the update font size listener and return the dialog fragment to the parent activity.
+                updateFontSizeListener.onApplyNewFontSize(this)
+
+                // Manually dismiss the alert dialog.
+                alertDialog.dismiss()
+
+                //Consume the event.
+                return@setOnKeyListener true
+            } else {  // If any other key was pressed do not consume the event.
+                return@setOnKeyListener false
+            }
+        }
+
+        // Return the alert dialog.
+        return alertDialog
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/HttpAuthenticationDialog.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/HttpAuthenticationDialog.java
deleted file mode 100644 (file)
index 9d7c0f3..0000000
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
- * Copyright © 2017-2020 Soren Stoutner <soren@stoutner.com>.
- *
- * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
- *
- * Privacy Browser 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 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.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.stoutner.privacybrowser.dialogs;
-
-import android.annotation.SuppressLint;
-import android.app.Dialog;
-import android.content.DialogInterface;
-import android.content.SharedPreferences;
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.preference.PreferenceManager;
-import android.text.SpannableStringBuilder;
-import android.text.Spanned;
-import android.text.style.ForegroundColorSpan;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.Window;
-import android.view.WindowManager;
-import android.webkit.HttpAuthHandler;
-import android.widget.EditText;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.appcompat.app.AlertDialog;
-import androidx.fragment.app.DialogFragment;
-
-import com.stoutner.privacybrowser.R;
-import com.stoutner.privacybrowser.activities.MainWebViewActivity;
-import com.stoutner.privacybrowser.fragments.WebViewTabFragment;
-import com.stoutner.privacybrowser.views.NestedScrollWebView;
-
-public class HttpAuthenticationDialog extends DialogFragment{
-    // Define the class variables.
-    private EditText usernameEditText;
-    private EditText passwordEditText;
-
-    public static HttpAuthenticationDialog displayDialog(String host, String realm, long webViewFragmentId) {
-        // Create an arguments bundle.
-        Bundle argumentsBundle = new Bundle();
-
-        // Store the variables in the bundle.
-        argumentsBundle.putString("host", host);
-        argumentsBundle.putString("realm", realm);
-        argumentsBundle.putLong("webview_fragment_id", webViewFragmentId);
-
-        // Create a new instance of the HTTP authentication dialog.
-        HttpAuthenticationDialog thisHttpAuthenticationDialog = new HttpAuthenticationDialog();
-
-        // Add the arguments bundle to the new dialog.
-        thisHttpAuthenticationDialog.setArguments(argumentsBundle);
-
-        // Return the new dialog.
-        return thisHttpAuthenticationDialog;
-    }
-
-    // `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`.
-    @SuppressLint("InflateParams")
-    @Override
-    @NonNull
-    public Dialog onCreateDialog(Bundle savedInstanceState) {
-        // Get a handle for the arguments.
-        Bundle arguments = getArguments();
-
-        // Remove the incorrect lint warning below that arguments might be null.
-        assert arguments != null;
-
-        // Get the variables from the bundle.
-        String httpAuthHost = arguments.getString("host");
-        String httpAuthRealm = arguments.getString("realm");
-        long webViewFragmentId = arguments.getLong("webview_fragment_id");
-
-        // Get the current position of this WebView fragment.
-        int webViewPosition = MainWebViewActivity.webViewPagerAdapter.getPositionForId(webViewFragmentId);
-
-        // Get the WebView tab fragment.
-        WebViewTabFragment webViewTabFragment = MainWebViewActivity.webViewPagerAdapter.getPageFragment(webViewPosition);
-
-        // Get the fragment view.
-        View fragmentView = webViewTabFragment.getView();
-
-        // Remove the incorrect lint warning below that the fragment view might be null.
-        assert fragmentView != null;
-
-        // Get a handle for the current WebView.
-        NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
-
-        // Get a handle for the HTTP authentication handler.
-        HttpAuthHandler httpAuthHandler = nestedScrollWebView.getHttpAuthHandler();
-
-        // Remove the incorrect lint warning that `getActivity()` might be null.
-        assert getActivity() != null;
-
-        // Get the activity's layout inflater.
-        LayoutInflater layoutInflater = getActivity().getLayoutInflater();
-
-        // Use an alert dialog builder to create the alert dialog.
-        AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity(), R.style.PrivacyBrowserAlertDialog);
-
-        // Get the current theme status.
-        int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
-
-        // Set the icon according to the theme.
-        if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
-            dialogBuilder.setIcon(R.drawable.lock_night);
-        } else {
-            dialogBuilder.setIcon(R.drawable.lock_day);
-        }
-
-        // Set the title.
-        dialogBuilder.setTitle(R.string.http_authentication);
-
-        // Set the layout.  The parent view is `null` because it will be assigned by `AlertDialog`.
-        dialogBuilder.setView(layoutInflater.inflate(R.layout.http_authentication_dialog, null));
-
-        // Setup the close button.
-        dialogBuilder.setNegativeButton(R.string.close, (DialogInterface dialog, int which) -> {
-            // Cancel the HTTP authentication request.
-            httpAuthHandler.cancel();
-
-            // Reset the HTTP authentication handler.
-            nestedScrollWebView.resetHttpAuthHandler();
-        });
-
-        // Setup the proceed button.
-        dialogBuilder.setPositiveButton(R.string.proceed, (DialogInterface dialog, int which) -> {
-            // Send the login information
-            login(httpAuthHandler);
-
-            // Reset the HTTP authentication handler.
-            nestedScrollWebView.resetHttpAuthHandler();
-        });
-
-        // Create an alert dialog from the alert dialog builder.
-        final AlertDialog alertDialog = dialogBuilder.create();
-
-        // Get the alert dialog window.
-        Window dialogWindow = alertDialog.getWindow();
-
-        // Remove the incorrect lint warning below that the dialog window might be null.
-        assert dialogWindow != null;
-
-        // Get a handle for the shared preferences.
-        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());
-
-        // Get the screenshot preference.
-        boolean allowScreenshots = sharedPreferences.getBoolean("allow_screenshots", false);
-
-        // Disable screenshots if not allowed.
-        if (!allowScreenshots) {
-            alertDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
-        }
-
-        // Display the keyboard.
-        dialogWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
-
-        // The alert dialog needs to be shown before the contents can be modified.
-        alertDialog.show();
-
-        // Get handles for the views.
-        TextView realmTextView = alertDialog.findViewById(R.id.http_authentication_realm);
-        TextView hostTextView = alertDialog.findViewById(R.id.http_authentication_host);
-        usernameEditText = alertDialog.findViewById(R.id.http_authentication_username);
-        passwordEditText = alertDialog.findViewById(R.id.http_authentication_password);
-
-        // Remove the incorrect lint warnings below that the views might be null.
-        assert realmTextView != null;
-        assert hostTextView != null;
-
-        // Set the realm text.
-        realmTextView.setText(httpAuthRealm);
-
-        // Set the realm text color according to the theme.  The deprecated `getResources()` must be used until API >= 23.
-        if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
-            realmTextView.setTextColor(getResources().getColor(R.color.gray_300));
-        } else {
-            realmTextView.setTextColor(getResources().getColor(R.color.black));
-        }
-
-        // Initialize the host label and the `SpannableStringBuilder`.
-        String hostLabel = getString(R.string.host) + "  ";
-        SpannableStringBuilder hostStringBuilder = new SpannableStringBuilder(hostLabel + httpAuthHost);
-
-        // Create a blue `ForegroundColorSpan`.
-        ForegroundColorSpan blueColorSpan;
-
-        // Set the blue color span according to the theme.  The deprecated `getResources()` must be used until API >= 23.
-        if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
-            blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.violet_500));
-        } else {
-            blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.blue_700));
-        }
-
-        // Setup the span to display the host name in blue.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
-        hostStringBuilder.setSpan(blueColorSpan, hostLabel.length(), hostStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
-
-        // Set the host text.
-        hostTextView.setText(hostStringBuilder);
-
-        // Allow the `enter` key on the keyboard to trigger `onHttpAuthenticationProceed` from `usernameEditText`.
-        usernameEditText.setOnKeyListener((View view, int keyCode, KeyEvent event) -> {
-            // If the event is a key-down on the `enter` key, call `onHttpAuthenticationProceed()`.
-            if ((keyCode == KeyEvent.KEYCODE_ENTER) && (event.getAction() == KeyEvent.ACTION_DOWN)) {
-                // Send the login information.
-                login(httpAuthHandler);
-
-                // Manually dismiss the alert dialog.
-                alertDialog.dismiss();
-
-                // Consume the event.
-                return true;
-            } else {  // If any other key was pressed, do not consume the event.
-                return false;
-            }
-        });
-
-        // Allow the `enter` key on the keyboard to trigger `onHttpAuthenticationProceed()` from `passwordEditText`.
-        passwordEditText.setOnKeyListener((View view, int keyCode, KeyEvent event) -> {
-            // If the event is a key-down on the `enter` key, call `onHttpAuthenticationProceed()`.
-            if ((keyCode == KeyEvent.KEYCODE_ENTER) && (event.getAction() == KeyEvent.ACTION_DOWN)) {
-                // Send the login information.
-                login(httpAuthHandler);
-
-                // Manually dismiss the alert dialog.
-                alertDialog.dismiss();
-
-                // Consume the event.
-                return true;
-            } else {  // If any other key was pressed, do not consume the event.
-                return false;
-            }
-        });
-
-        // Return the alert dialog.
-        return alertDialog;
-    }
-
-    private void login(HttpAuthHandler httpAuthHandler) {
-        // Send the login information.
-        httpAuthHandler.proceed(usernameEditText.getText().toString(), passwordEditText.getText().toString());
-    }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/HttpAuthenticationDialog.kt b/app/src/main/java/com/stoutner/privacybrowser/dialogs/HttpAuthenticationDialog.kt
new file mode 100644 (file)
index 0000000..7c94fcf
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+ * Copyright © 2017-2020 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+ *
+ * Privacy Browser 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 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.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.stoutner.privacybrowser.dialogs
+
+import android.annotation.SuppressLint
+import android.app.Dialog
+import android.content.DialogInterface
+import android.content.res.Configuration
+import android.os.Bundle
+import android.text.SpannableStringBuilder
+import android.text.Spanned
+import android.text.style.ForegroundColorSpan
+import android.view.KeyEvent
+import android.view.View
+import android.view.WindowManager
+import android.webkit.HttpAuthHandler
+import android.widget.EditText
+import android.widget.TextView
+
+import androidx.appcompat.app.AlertDialog
+import androidx.fragment.app.DialogFragment
+import androidx.preference.PreferenceManager
+
+import com.stoutner.privacybrowser.R
+import com.stoutner.privacybrowser.activities.MainWebViewActivity
+import com.stoutner.privacybrowser.views.NestedScrollWebView
+
+// Declare the class constants.
+private const val HOST = "host"
+private const val REALM = "realm"
+private const val WEBVIEW_FRAGMENT_ID = "webview_fragment_id"
+
+class HttpAuthenticationDialog: DialogFragment() {
+    // Define the class variables.
+    private var dismissDialog: Boolean = false
+
+    // Define the class views.
+    private lateinit var usernameEditText: EditText
+    private lateinit var passwordEditText: EditText
+
+    companion object {
+        // `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin.  Also, the function can then be moved out of a companion object and just become a package-level function.
+        @JvmStatic
+        fun displayDialog(host: String, realm: String, webViewFragmentId: Long): HttpAuthenticationDialog {
+            // Create an arguments bundle.
+            val argumentsBundle = Bundle()
+
+            // Store the variables in the bundle.
+            argumentsBundle.putString(HOST, host)
+            argumentsBundle.putString(REALM, realm)
+            argumentsBundle.putLong(WEBVIEW_FRAGMENT_ID, webViewFragmentId)
+
+            // Create a new instance of the HTTP authentication dialog.
+            val httpAuthenticationDialog = HttpAuthenticationDialog()
+
+            // Add the arguments bundle to the dialog.
+            httpAuthenticationDialog.arguments = argumentsBundle
+
+            // Return the new dialog.
+            return httpAuthenticationDialog
+        }
+    }
+
+    // `@SuppressLint("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`.
+    @SuppressLint("InflateParams")
+    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+        // Get a handle for the arguments.
+        val arguments = requireArguments()
+
+        // Get the variables from the bundle.
+        val httpAuthHost = arguments.getString(HOST)
+        val httpAuthRealm = arguments.getString(REALM)
+        val webViewFragmentId = arguments.getLong(WEBVIEW_FRAGMENT_ID)
+
+        // Try to populate the alert dialog.
+        try {  // Getting the WebView tab fragment will fail if Privacy Browser has been restarted.
+            // Get the current position of this WebView fragment.
+            val webViewPosition = MainWebViewActivity.webViewPagerAdapter.getPositionForId(webViewFragmentId)
+
+            // Get the WebView tab fragment.
+            val webViewTabFragment = MainWebViewActivity.webViewPagerAdapter.getPageFragment(webViewPosition)
+
+            // Get the fragment view.
+            val fragmentView = webViewTabFragment.requireView()
+
+            // Get a handle for the current WebView.
+            val nestedScrollWebView = fragmentView.findViewById<NestedScrollWebView>(R.id.nestedscroll_webview)
+
+            // Get a handle for the HTTP authentication handler.
+            val httpAuthHandler = nestedScrollWebView.httpAuthHandler
+
+            // Use an alert dialog builder to create the alert dialog.
+            val dialogBuilder = AlertDialog.Builder(requireActivity(), R.style.PrivacyBrowserAlertDialog)
+
+            // Get the current theme status.
+            val currentThemeStatus = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
+
+            // Set the icon according to the theme.
+            if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+                dialogBuilder.setIcon(R.drawable.lock_day)
+            } else {
+
+                dialogBuilder.setIcon(R.drawable.lock_night)
+            }
+
+            // Set the title.
+            dialogBuilder.setTitle(R.string.http_authentication)
+
+            // Get the activity's layout inflater.
+            val layoutInflater = requireActivity().layoutInflater
+
+            // Set the layout.  The parent view is `null` because it will be assigned by the alert dialog.
+            dialogBuilder.setView(layoutInflater.inflate(R.layout.http_authentication_dialog, null))
+
+            // Set the close button listener.
+            dialogBuilder.setNegativeButton(R.string.close) { _: DialogInterface?, _: Int ->
+                // Cancel the HTTP authentication request.
+                httpAuthHandler.cancel()
+
+                // Reset the HTTP authentication handler.
+                nestedScrollWebView.resetHttpAuthHandler()
+            }// Set the proceed button listener.
+            dialogBuilder.setPositiveButton(R.string.proceed) { _: DialogInterface?, _: Int ->
+                // Send the login information
+                login(httpAuthHandler)
+
+                // Reset the HTTP authentication handler.
+                nestedScrollWebView.resetHttpAuthHandler()
+            }
+
+            // Create an alert dialog from the alert dialog builder.
+            val alertDialog = dialogBuilder.create()
+
+            // Get the alert dialog window.
+            val dialogWindow = alertDialog.window!!
+
+            // Get a handle for the shared preferences.
+            val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
+
+            // Get the screenshot preference.
+            val allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false)
+
+            // Disable screenshots if not allowed.
+            if (!allowScreenshots) {
+                alertDialog.window!!.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
+            }
+
+            // Display the keyboard.
+            dialogWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)
+
+            // The alert dialog needs to be shown before the contents can be modified.
+            alertDialog.show()
+
+            // Get handles for the views.
+            val realmTextView = alertDialog.findViewById<TextView>(R.id.http_authentication_realm)!!
+            val hostTextView = alertDialog.findViewById<TextView>(R.id.http_authentication_host)!!
+            usernameEditText = alertDialog.findViewById(R.id.http_authentication_username)!!
+            passwordEditText = alertDialog.findViewById(R.id.http_authentication_password)!!
+
+            // Set the realm text.
+            realmTextView.text = httpAuthRealm
+
+            // Initialize the host label and the spannable string builder.
+            val hostLabel = getString(R.string.host) + "  "
+            val hostStringBuilder = SpannableStringBuilder(hostLabel + httpAuthHost)
+
+            // Create a blue foreground color span.
+            val blueColorSpan: ForegroundColorSpan
+
+            // Set the blue color span according to the theme.  The deprecated `getColor()` must be used until API >= 23.
+            blueColorSpan = if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+                @Suppress("DEPRECATION")
+                ForegroundColorSpan(resources.getColor(R.color.blue_700))
+            } else {
+                @Suppress("DEPRECATION")
+                ForegroundColorSpan(resources.getColor(R.color.violet_500))
+            }
+
+            // Setup the span to display the host name in blue.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
+            hostStringBuilder.setSpan(blueColorSpan, hostLabel.length, hostStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
+
+            // Set the host text.
+            hostTextView.text = hostStringBuilder
+
+            // Allow the enter key on the keyboard to send the login information from the username edit text.
+            usernameEditText.setOnKeyListener { _: View?, keyCode: Int, event: KeyEvent ->
+                // Check the key code and event.
+                if (keyCode == KeyEvent.KEYCODE_ENTER && event.action == KeyEvent.ACTION_DOWN) {  // The enter key was pressed.
+                    // Send the login information.
+                    login(httpAuthHandler)
+
+                    // Manually dismiss the alert dialog.
+                    alertDialog.dismiss()
+
+                    // Consume the event.
+                    return@setOnKeyListener true
+                } else {  // If any other key was pressed, do not consume the event.
+                    return@setOnKeyListener false
+                }
+            }
+
+            // Allow the enter key on the keyboard to send the login information from the password edit text.
+            passwordEditText.setOnKeyListener { _: View?, keyCode: Int, event: KeyEvent ->
+                // Check the key code and event.
+                if (keyCode == KeyEvent.KEYCODE_ENTER && event.action == KeyEvent.ACTION_DOWN) {  // The enter key was pressed.
+                    // Send the login information.
+                    login(httpAuthHandler)
+
+                    // Manually dismiss the alert dialog.
+                    alertDialog.dismiss()
+
+                    // Consume the event.
+                    return@setOnKeyListener true
+                } else {  // If any other key was pressed, do not consume the event.
+                    return@setOnKeyListener false
+                }
+            }
+
+            // Return the alert dialog.
+            return alertDialog
+        } catch (exception: Exception) {  // Privacy Browser was restarted and the HTTP auth handler no longer exists.
+            // Use an alert dialog builder to create an empty alert dialog.
+            val dialogBuilder = AlertDialog.Builder(requireActivity(), R.style.PrivacyBrowserAlertDialog)
+
+            // Create an empty alert dialog from the alert dialog builder.
+            val alertDialog = dialogBuilder.create()
+
+            // Set the flag to dismiss the dialog as soon as it is resumed.
+            dismissDialog = true
+
+            // Return the alert dialog.
+            return alertDialog
+        }
+    }
+
+    override fun onResume() {
+        // Run the default command.
+        super.onResume()
+
+        // Dismiss the alert dialog if the activity was restarted and the HTTP auth handler no longer exists.
+        if (dismissDialog) {
+            dialog!!.dismiss()
+        }
+    }
+
+    private fun login(httpAuthHandler: HttpAuthHandler) {
+        // Send the login information.
+        httpAuthHandler.proceed(usernameEditText.text.toString(), passwordEditText.text.toString())
+    }
+}
\ No newline at end of file
index 2406e95af31d69582f8fe7606cbe1d9e543c88f8..25701f7792fd4a76ec5fc1734d17b22e2cecd6f2 100644 (file)
@@ -74,7 +74,7 @@ public class MoveToFolderDialog extends DialogFragment {
         moveToFolderListener = (MoveToFolderListener) context;
     }
 
-    // `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`.
+    // `@SuppressLint("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`.
     @SuppressLint("InflateParams")
     @Override
     @NonNull
index 49c15394480b42ce96bbf74fe72c7cdfffe08cbf..80b8b4e771949bb7b3bc014081aa17d41a9f4c72 100644 (file)
@@ -103,7 +103,7 @@ public class PinnedMismatchDialog extends DialogFragment {
         return pinnedMismatchDialog;
     }
 
-    // `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`.
+    // `@SuppressLint("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`.
     @SuppressLint("InflateParams")
     @Override
     @NonNull
index c73cfdc6ecd6fe897bc71401d573bdb051769f72..a3cf61bffefbbb0757d7e81b4ead047dc22f6bd7 100644 (file)
@@ -70,7 +70,7 @@ public class SaveLogcatDialog extends DialogFragment {
         saveLogcatListener = (SaveLogcatListener) context;
     }
 
-    // `@SuppressLing("InflateParams")` removes the warning about using null as the parent view group when inflating the alert dialog.
+    // `@SuppressLint("InflateParams")` removes the warning about using null as the parent view group when inflating the alert dialog.
     @SuppressLint("InflateParams")
     @Override
     @NonNull
index 3a68a6169fc3d82394018be1f7038a38624df2e8..76a23a815420101b0564c473b9818b4ee694878d 100644 (file)
@@ -96,7 +96,7 @@ public class SslCertificateErrorDialog extends DialogFragment {
         return thisSslCertificateErrorDialog;
     }
 
-    // `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`.
+    // `@SuppressLint("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`.
     @SuppressLint("InflateParams")
     @Override
     @NonNull
index 661df9a20714a2b8a6822ca532d8c1d259156fea..3f8035e0b51e5aae9a19268ba36ded4b388536b0 100644 (file)
@@ -88,7 +88,7 @@ public class UrlHistoryDialog extends DialogFragment{
 
     @Override
     @NonNull
-    // `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`.
+    // `@SuppressLint("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`.
     @SuppressLint("InflateParams")
     public Dialog onCreateDialog(Bundle savedInstanceState) {
         // Get the activity's layout inflater.
index 0ff75520ce7f70c6962d1de9cb9b380e63de0f05..c862159e0993f65444aa36fedbd6a88b23e926e5 100644 (file)
@@ -77,7 +77,7 @@ public class ViewRequestDialog extends DialogFragment {
 
     @Override
     @NonNull
-    // `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`.
+    // `@SuppressLint("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`.
     @SuppressLint("InflateParams")
     public Dialog onCreateDialog(Bundle savedInstanceState) {
         // Remove the incorrect lint warning that `getInt()` might be null.
index c382c05dd8812b9e4a0df7ca669c5f25d89a31a2..d27b1930cdbf2aa079d90b4a8688678d757739d7 100644 (file)
@@ -70,7 +70,7 @@ public class ViewSslCertificateDialog extends DialogFragment {
         return viewSslCertificateDialog;
     }
 
-    // `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the alert dialog.
+    // `@SuppressLint("InflateParams")` removes the warning about using `null` as the parent view group when inflating the alert dialog.
     @SuppressLint("InflateParams")
     @Override
     @NonNull
index b413584e5786bbe2e1e2bed797cdb64febe94ba9..d87385db5666f7fb7ea44a1a8298f08d29d20190 100644 (file)
@@ -35,7 +35,7 @@ import androidx.preference.PreferenceManager;
 import com.stoutner.privacybrowser.R;
 
 public class WaitingForProxyDialog extends DialogFragment {
-    // `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the alert dialog.
+    // `@SuppressLint("InflateParams")` removes the warning about using `null` as the parent view group when inflating the alert dialog.
     @SuppressLint("InflateParams")
     @Override
     @NonNull
index 35d626935039f0c5a151df734ba148bf83d53c58..5e3a433643526c8b5d707b70b4fa9f7e37537d13 100644 (file)
@@ -307,7 +307,7 @@ public class DomainSettingsFragment extends Fragment {
         final ForegroundColorSpan redColorSpan;
         final ForegroundColorSpan blueColorSpan;
 
-        // Set the color spans according to the theme.  The deprecated `resources` must be used until the minimum API >= 23.
+        // Set the color spans according to the theme.  The deprecated `getColor()` must be used until the minimum API >= 23.
         if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
             redColorSpan = new ForegroundColorSpan(resources.getColor(R.color.red_900));
             blueColorSpan = new ForegroundColorSpan(resources.getColor(R.color.violet_500));
index a03d0095c379643d9c73c8ddcdb93789b4a8f6c6..4c074a4988a357950e6b2e1f101897261cff43fa 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 
 <!--
-  Copyright © 2016-2019 Soren Stoutner <soren@stoutner.com>.
+  Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
 
   This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
 
@@ -26,7 +26,6 @@
 
     <!-- Setting `android:focusable` and `android:focusableInTouchMode` prevent `edit_bookmark_display_order_edittext` from being autoselected. -->
     <LinearLayout
-        xmlns:tools="http://schemas.android.com/tools"
         android:layout_height="wrap_content"
         android:layout_width="match_parent"
         android:orientation="vertical"
index 817ed9bd3e045174507a72404cc10ec4e76917b3..43a427d1d6d8eecf1ee2d02cbd5a8d2c859444dc 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 
 <!--
-  Copyright © 2016-2017,2019 Soren Stoutner <soren@stoutner.com>.
+  Copyright © 2016-2017,2019-2020 Soren Stoutner <soren@stoutner.com>.
 
   This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
 
 
 <ScrollView
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
     android:layout_height="wrap_content"
     android:layout_width="match_parent" >
 
     <LinearLayout
-        xmlns:tools="http://schemas.android.com/tools"
         android:layout_height="wrap_content"
         android:layout_width="match_parent"
         android:orientation="vertical" >
@@ -88,7 +88,7 @@
             </RadioGroup>
         </LinearLayout>
 
-        <!-- The `TextInputLayout` makes the `android:hint` float above the `EditText`. -->
+        <!-- The `TextInputLayout` makes the `android:hint` float above the edit text. -->
         <com.google.android.material.textfield.TextInputLayout
             android:layout_height="wrap_content"
             android:layout_width="match_parent"
@@ -97,7 +97,7 @@
             android:layout_marginStart="4dp"
             android:layout_marginEnd="4dp" >
 
-            <!-- `android:imeOptions="actionGo"` sets the keyboard to have a `go` key instead of a `new line` key.  `android:inputType="textUri"` disables spell check in the `EditText`. -->
+            <!-- `android:imeOptions="actionGo"` sets the keyboard to have a `go` key instead of a `new line` key.  `android:inputType="textUri"` disables spell check in the edit text. -->
             <com.google.android.material.textfield.TextInputEditText
                 android:id="@+id/edit_bookmark_name_edittext"
                 android:layout_height="wrap_content"
                 android:selectAllOnFocus="true" />
         </com.google.android.material.textfield.TextInputLayout>
 
-        <!-- The `TextInputLayout` makes the `android:hint` float above the `EditText`. -->
+        <!-- The `TextInputLayout` makes the `android:hint` float above the edit text. -->
         <com.google.android.material.textfield.TextInputLayout
             android:layout_height="wrap_content"
             android:layout_width="match_parent"
             android:layout_marginStart="4dp"
             android:layout_marginEnd="4dp" >
 
-            <!-- `android:imeOptions="actionGo"` sets the keyboard to have a `go` key instead of a `new line` key.  `android:inputType="textUri"` disables spell check in the `EditText`. -->
+            <!-- `android:imeOptions="actionGo"` sets the keyboard to have a `go` key instead of a `new line` key.  `android:inputType="textUri"` disables spell check in the edit text. -->
             <com.google.android.material.textfield.TextInputEditText
                 android:id="@+id/edit_bookmark_url_edittext"
                 android:layout_height="wrap_content"
index bb59f8b9ccca486e342ab2fb72acc92a64fecf80..49d3d7d5b9b9f53551f1ded2802086b768fe6105 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 
 <!--
-  Copyright © 2016-2019 Soren Stoutner <soren@stoutner.com>.
+  Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
 
   This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
 
@@ -26,7 +26,6 @@
 
     <!-- Setting `android:focusable` and `android:focusableInTouchMode` prevent `edit_bookmark_display_order_edittext` from being autoselected. -->
     <LinearLayout
-        xmlns:tools="http://schemas.android.com/tools"
         android:layout_height="wrap_content"
         android:layout_width="match_parent"
         android:orientation="vertical"
index 997a8b455a36cac02559bad138d430420e12070b..9a0b397727fd437cb73daa4981eb6a18311d9a4e 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 
 <!--
-  Copyright © 2016-2017,2019 Soren Stoutner <soren@stoutner.com>.
+  Copyright © 2016-2017,2019-2020 Soren Stoutner <soren@stoutner.com>.
 
   This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
 
 
 <ScrollView
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
     android:layout_height="wrap_content"
     android:layout_width="match_parent" >
 
     <LinearLayout
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:tools="http://schemas.android.com/tools"
         android:layout_height="wrap_content"
         android:layout_width="match_parent"
         android:orientation="vertical" >
index be1d878867ee20992bf6b56b7bda39858dd66baf..6698e2152e55e5a7a92efcfdb6b9069cad1a7028 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 
 <!--
-  Copyright © 2017,2019 Soren Stoutner <soren@stoutner.com>.
+  Copyright © 2017,2019-2020 Soren Stoutner <soren@stoutner.com>.
 
   This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
 
@@ -37,7 +37,8 @@
             android:layout_marginTop="12dp"
             android:layout_gravity="center_horizontal"
             android:textSize="26sp"
-            android:textStyle="bold" />
+            android:textStyle="bold"
+            android:textColor="?android:textColorPrimary" />
 
         <TextView
             android:id="@+id/http_authentication_host"
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
new file mode 100644 (file)
index 0000000..7a13f95
--- /dev/null
@@ -0,0 +1,603 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  Copyright © 2015-2020 Soren Stoutner <soren@stoutner.com>.
+
+  Translation 2020 Thiago Nazareno Conceição Silva de Jesus <mochileiro2006-trilhas@yahoo.com.br>.  Copyright assigned to Soren Stoutner <soren@stoutner.com>.
+
+  This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+
+  Privacy Browser 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 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.  If not, see <http://www.gnu.org/licenses/>. -->
+
+<resources>
+    <!-- Activities. -->
+    <string name="privacy_browser">Navegador Privado</string>
+    <string name="short_name">Privado</string>
+    <!-- For translations, `android_asset_path` should be the localization abbreviation.  For example, Spanish is `es`.  This should not be translated unless the Guide and About sections are localized. -->
+    <!-- <string name="android_asset_path">pt-br</string> -->
+
+    <!-- MainWebView. -->
+    <string name="privacy_mode">Modo Privado</string>
+    <string name="javascript_enabled">JavaScript ativado</string>
+    <string name="javascript_disabled">JavaScript desativado</string>
+    <string name="first_party_cookies_enabled">Cookies primários habilitados</string>
+    <string name="first_party_cookies_disabled">Cookies primários desabilitados</string>
+    <string name="third_party_cookies_enabled">Cookies de terceiros habilitados</string>
+    <string name="third_party_cookies_disabled">Cookies de terceiros desabilitados</string>
+    <string name="dom_storage_enabled">Armazenamento DOM ativado</string>
+    <string name="dom_storage_disabled">Armazenamento DOM desativado</string>
+    <string name="form_data_enabled">Dados de formulário habilitados</string>  <!-- The form data strings can be removed once the minimum API >= 26. -->
+    <string name="form_data_disabled">Dados de formulário desabilitados</string>  <!-- The form data strings can be removed once the minimum API >= 26. -->
+    <string name="cookies_deleted">Cookies excluídos</string>
+    <string name="dom_storage_deleted">Armazenamento DOM excluído</string>
+    <string name="form_data_deleted">Dados do formulário excluídos</string>
+    <string name="open_navigation_drawer">Abrir a caixa de navegação</string>
+    <string name="close_navigation_drawer">Fechar  a caixa de navegação</string>
+    <string name="unrecognized_url">URL desconhecida:</string>
+    <string name="add_tab">Adicionar aba</string>
+    <string name="close_tab">Fechar aba</string>
+    <string name="new_tab">Nova aba</string>
+    <string name="loading">Carregando...</string>
+    <string name="error">Erro:</string>
+    <string name="apply">Aplicar</string>
+
+    <!-- Loading Blocklists. -->
+    <string name="loading_easylist">Carregando  EasyList</string>
+    <string name="loading_easyprivacy">Carregando EasyPrivacy</string>
+    <string name="loading_fanboys_annoyance_list">Carregando Fanboy’s Annoyance List</string>
+    <string name="loading_fanboys_social_blocking_list">Carregando Fanboy’s Social Blocking List</string>
+    <string name="loading_ultralist">Carregando UltraList</string>
+    <string name="loading_ultraprivacy">Carregando UltraPrivacy</string>
+
+    <!-- Custom App Bar. -->
+    <string name="favorite_icon">Ícone dos Favoritos</string>
+    <string name="url_or_search_terms">URL ou termos de pesquisa</string>
+
+    <!-- View SSL Certificate. -->
+    <string name="view_ssl_certificate">Ver certificado SSL</string>
+    <string name="unencrypted_website">Site não criptografado</string>
+    <string name="no_ssl_certificate">A comunicação com este site não é criptografada. Isso permite que terceiros interceptem informações, rastreiem sua navegação e insiram conteúdo malicioso.</string>
+    <string name="ssl_certificate">Certificado SSL</string>
+    <string name="close">Fecha</string>
+    <string name="domain">Domínio</string>
+    <string name="domain_label">Domínio:</string>
+    <string name="ip_addresses">Endereço de IP:</string>
+    <string name="issued_to">Emitido para</string>
+    <string name="issued_by">Emitido por</string>
+    <string name="common_name">Nome Comum:</string>
+    <string name="organization">Organização:</string>
+    <string name="organizational_unit">Unidade da  Organização:</string>
+    <string name="valid_dates">Datas Válidas</string>
+    <string name="start_date">Data Inicial:</string>
+    <string name="end_date">Data Final:</string>
+
+    <!-- SSL Certificate Error. -->
+    <string name="ssl_certificate_error">Erro de certificado SSL</string>
+    <string name="proceed">Prosseguir</string>
+    <string name="future_certificate">A data de início do certificado está no futuro</string>
+    <string name="expired_certificate">O certificado expirou</string>
+    <string name="cn_mismatch">O nome comum não corresponde ao nome do host</string>
+    <string name="untrusted">A autoridade de certificação não é confiável</string>
+    <string name="invalid_date">A data do certificado é inválida</string>
+    <string name="invalid_certificate">O certificado é inválido</string>
+    <string name="url">URL</string>
+    <string name="url_label">URL:</string>
+
+    <!-- Pinned Mismatch. -->
+    <string name="pinned_mismatch">Incompatibilidade fixada</string>
+    <string name="update">Atualizar</string>
+    <string name="current">Atual</string>
+    <string name="pinned">Fixada</string>
+
+    <!-- HTTP Authentication. -->
+    <string name="http_authentication">Autenticação HTTP</string>
+    <string name="host">Host:</string>
+    <string name="username">Nome do usuário</string>
+    <string name="password">Senha</string>
+
+    <!-- MainWebViewActivity Navigation Menu. -->
+    <string name="navigation_drawer">Caixa de Navegação</string>
+    <string name="navigation">Navegação</string>
+    <string name="clear_and_exit">Limpar e fechar</string>
+    <string name="home">Início</string>
+    <string name="back">Fim</string>
+    <string name="forward">Avançar</string>
+    <string name="history">Histórico</string>
+    <string name="clear_history">Limpar Histórico</string>
+    <string name="open">Abrir</string>
+    <string name="downloads">Downloads</string>
+    <string name="settings">Configurações</string>
+    <string name="import_export">Importar/Exportar</string>
+    <string name="logcat">Logcat</string>
+    <string name="guide">Guia</string>
+    <string name="about">Sobre</string>
+
+    <!-- MainWebViewActivity Options Menu. -->
+    <string name="javascript">JavaScript</string>
+    <string name="refresh">Atualizar</string>
+    <string name="stop">Parar</string>
+    <string name="first_party_cookies">Cookies primários</string>
+    <string name="third_party_cookies">Cookies de terceiros</string>
+    <string name="dom_storage">Armazenamento DOM</string>
+    <string name="form_data">Dados do formulário</string>  <!-- The form data strings can be removed once the minimum API >= 26. -->
+    <string name="clear_data">Limpar dados</string>
+    <string name="clear_cookies">Limpar Cookies</string>
+    <string name="clear_dom_storage">limpar Armazenamento DOM</string>
+    <string name="clear_form_data">Clear Form Data</string>  <!-- The form data strings can be removed once the minimum API >= 26. -->
+    <string name="options_fanboys_annoyance_list">Lista de Aborrecimento Fanboy’s</string>
+    <string name="options_fanboys_social_blocking_list">Lista de bloqueio social Fanboy’s</string>
+    <string name="options_block_all_third_party_requests">Bloquear todas as solicitações de terceiros</string>
+    <string name="page">Página</string>
+    <string name="options_user_agent">User Agent</string>
+    <string name="user_agent_privacy_browser">Privacy Browser</string>
+    <string name="user_agent_webview_default">WebView Default</string>
+    <string name="user_agent_firefox_on_android">Firefox para Android</string>
+    <string name="user_agent_chrome_on_android">Chrome para Android</string>
+    <string name="user_agent_safari_on_ios">Safari para iOS</string>
+    <string name="user_agent_firefox_on_linux">Firefox para Linux</string>
+    <string name="user_agent_chromium_on_linux">Chromium para Linux</string>
+    <string name="user_agent_firefox_on_windows">Firefox para Windows</string>
+    <string name="user_agent_chrome_on_windows">Chrome para Windows</string>
+    <string name="user_agent_edge_on_windows">Edge para Windows</string>
+    <string name="user_agent_internet_explorer_on_windows">Internet Explorer para Windows</string>
+    <string name="user_agent_safari_on_macos">Safari para macOS</string>
+    <string name="user_agent_custom">Personalizado</string>
+    <string name="swipe_to_refresh_options_menu">Deslize para atualizar</string>
+    <string name="wide_viewport">Janela ampliada</string>
+    <string name="display_images">Exibir imagens</string>
+    <string name="dark_webview">Dark WebView</string>
+    <string name="font_size">Tamanho da Fonte</string>
+    <string name="find_on_page">Procurar</string>
+    <string name="print">Imprimir</string>
+    <string name="privacy_browser_web_page">Página da Web do Privacy Browser</string>
+    <string name="save">Salvar</string>
+    <string name="save_url">Salvar URL</string>
+    <string name="save_as_archive">Salvar como arquivo</string>
+    <string name="save_as_image">Salvar como imagem</string>
+    <string name="add_to_home_screen">Adicionar à tela inicial</string>
+    <string name="view_source">Ver fonte</string>
+    <string name="share">Compartilhar</string>
+    <string name="share_url">Compartilhar URL</string>
+    <string name="open_with_app">Abrir com aplicativo</string>
+    <string name="open_with_browser">Abrir com navegador</string>
+    <string name="add_domain_settings">Adicionar configurações de domínio</string>
+    <string name="edit_domain_settings">Editar configurações de domínio</string>
+
+    <!-- Context Menus. -->
+    <string name="open_in_new_tab">Abrir em nova aba</string>
+    <string name="open_in_background">Abrir em segundo plano</string>
+    <string name="open_image_in_new_tab">Abra a imagem em uma nova aba</string>
+    <string name="copy_url">Copiar URL</string>
+    <string name="email_address">Endereço de Email</string>
+    <string name="copy_email_address">Copiar Email Address</string>
+    <string name="write_email">Escrever Email</string>
+    <string name="view_image">Visualizar Imagem</string>
+
+    <!-- Find on Page. -->
+    <string name="previous">Anterior</string>
+    <string name="next">Próximo</string>
+
+    <!-- Save. -->
+    <string name="file_name">Nome do Arquivo</string>
+    <string name="save_archive">Salvar Arquivo</string>
+    <string name="save_image">Salvar Imagem</string>
+    <string name="webpage_mht">Pagina_Web.mht</string>
+    <string name="webpage_png">Pagina_Web.png</string>
+    <string name="file">Arquivo</string>
+    <string name="bytes">bytes</string>
+    <string name="unknown_size">tamanho desconhecido</string>
+    <string name="invalid_url">URL inválida</string>
+    <string name="ok">OK</string>
+    <string name="saving_file">Salvando file:</string>
+    <string name="saving_image">Salvando imagem:</string>
+    <string name="file_saved">Arquivo Salvo:</string>
+    <string name="image_saved">Imagem salva.</string>
+    <string name="error_saving_file">Erro ao salvar o arquivo:</string>
+    <string name="error_saving_image">Erro ao salvar a imagem:</string>
+
+    <!-- View Source. -->
+    <string name="request_headers">Solicitar cabeçalhos</string>
+    <string name="response_message">Mensagem de Resposta</string>
+    <string name="response_headers">Cabeçalhos de resposta</string>
+    <string name="response_body">Corpo de Resposta</string>
+    <string name="about_view_source">Sobre Ver Fonte</string>
+    <string name="about_view_source_message">Como o WebView do Android não expõe as informações de origem,
+        uma solicitação separada foi feita usando ferramentas do sistema para reunir as informações exibidas nesta atividade.
+        Pode haver algumas diferenças entre esses dados e aqueles usados pelo WebView na atividade principal. Essa limitação será removida na série 4.x com o lançamento do Privacy WebView.</string>
+
+    <!-- Create Home Screen Shortcut Alert Dialog. -->
+    <string name="create_shortcut">Criar atalho</string>
+    <string name="shortcut_name">Nome do atalho</string>
+    <string name="open_with_default_browser">Abrir com o navegador padrão.</string>
+    <string name="open_with_privacy_browser">Abrir com o  Privacy Browser.</string>
+    <string name="cancel">Cancelar</string>
+    <string name="create">Criar</string>
+
+    <!-- Bookmarks. -->
+    <string name="bookmarks">Favoritos</string>
+    <string name="database_view">Visualização do banco de dados</string>
+    <string name="create_bookmark">Criar marcador</string>
+    <string name="create_folder">Criar pasta</string>
+    <string name="current_bookmark_icon">Ícone de favorito atual</string>
+    <string name="current_folder_icon">Ícone da pasta atual</string>
+    <string name="default_folder_icon">Ícone de pasta padrão</string>
+    <string name="web_page_favorite_icon">Ícone favorito da página da web</string>
+    <string name="bookmark_name">Nome do marcador</string>
+    <string name="folder_name">Nome da pasta</string>
+    <string name="bookmark_url">URL do favorito</string>
+    <string name="folder_names_must_be_unique">Os nomes das pastas devem ser únicos</string>
+    <string name="edit_bookmark">Editar favorito</string>
+    <string name="edit_folder">Editar Pasta</string>
+    <string name="move_to_folder">Mover para a pasta</string>
+    <string name="move">Mover</string>
+
+    <!-- Bookmarks Contextual App Bar. -->
+    <string name="selected">Selecionados:</string>  <!--This is a plural adjective.-->
+    <string name="move_up">Subir</string>
+    <string name="move_down">Descer</string>
+    <string name="edit">Editar</string>
+    <string name="delete">Apagar</string>
+    <string name="select_all">Selecionar Tudo</string>
+    <string name="bookmarks_deleted">Favoritos excluídos:</string>
+    <string name="undo">Desfazer</string>
+
+    <!-- Bookmarks Database View. -->
+    <string name="bookmarks_database_view">Visualização do banco de dados de favoritos</string>
+    <string name="all_folders">Todas as pastas</string>
+    <string name="home_folder">Pasta Pessoal</string>
+    <string name="sort">Ordenar</string>
+    <string name="sorted_by_database_id">Ordenado por ID de banco de dados.</string>
+    <string name="sorted_by_display_order">Ordenado por ordem de exibição.</string>
+    <string name="database_id">ID de banco de dados:</string>
+    <string name="folder">Pasta:</string>
+    <string name="parent_folder">Pasta Superior:</string>
+    <string name="display_order">Ordem de exibição:</string>
+    <string name="cannot_deselect_bookmark">Um favorito não pode ser desmarcado enquanto a pasta superior estiver selecionada.</string>
+
+    <!-- Requests. -->
+    <string name="requests">Solicitações</string>
+    <string name="request_details">Solicitar Detalhes</string>
+    <string name="disposition">Disposição</string>
+    <string name="all">Tudo</string>
+    <string name="default_label">Padrão</string>
+    <string name="default_allowed">Padrão - Permitido</string>
+    <string name="allowed">Permitido</string>
+    <string name="allowed_plural">Permitido</string>
+    <string name="third_party_plural">Terceiros</string>
+    <string name="third_party_blocked">Terceiros - Bloqueados</string>
+    <string name="blocked">Bloqueado</string>
+    <string name="blocked_plural">Bloqueados</string>
+    <string name="blocklist">Lista de bloqueios</string>
+    <string name="sublist">Sublista</string>
+    <string name="main_whitelist">Lista branca principal</string>
+    <string name="final_whitelist">Lista de permissões final</string>
+    <string name="domain_whitelist">Lista de permissões de domínio</string>
+    <string name="domain_initial_whitelist">Lista de permissões inicial do domínio</string>
+    <string name="domain_final_whitelist">Lista de permissões final de domínio</string>
+    <string name="third_party_whitelist">Lista de permissões de terceiros</string>
+    <string name="third_party_domain_whitelist">Lista de permissões de domínio de terceiros</string>
+    <string name="third_party_domain_initial_whitelist">Lista de permissões inicial de domínio de terceiros</string>
+    <string name="main_blacklist">Lista negra principal</string>
+    <string name="initial_blacklist">Lista negra inicial</string>
+    <string name="final_blacklist">Lista negra final</string>
+    <string name="domain_blacklist">Lista negra de domínio</string>
+    <string name="domain_initial_blacklist">Lista negra inicial do domínio</string>
+    <string name="domain_final_blacklist">Lista negra final do domínio</string>
+    <string name="domain_regular_expression_blacklist">Lista negra de expressões regulares de domínio</string>
+    <string name="third_party_blacklist">Lista negra de terceiros</string>
+    <string name="third_party_initial_blacklist">Lista negra inicial de terceiros</string>
+    <string name="third_party_domain_blacklist">Lista negra de domínios de terceiros</string>
+    <string name="third_party_domain_initial_blacklist">Lista negra inicial de domínios de terceiros</string>
+    <string name="third_party_regular_expression_blacklist">Lista negra de expressões regulares de terceiros</string>
+    <string name="third_party_domain_regular_expression_blacklist">Lista negra de expressões regulares de domínios de terceiros</string>
+    <string name="regular_expression_blacklist">Lista negra de expressões regulares</string>
+    <string name="blocklist_entries">Entradas da lista de bloqueio</string>
+    <string name="blocklist_original_entry">Entrada original da lista de bloqueio</string>
+
+    <!-- Domains. -->
+    <string name="domains">Domínios</string>
+    <string name="domain_settings">Configurações de domínio</string>
+    <string name="add_domain">Adicionar Domínio</string>
+    <string name="domain_name_already_exists">Nome de domínio já existe</string>
+    <string name="add">Adicionar</string>
+    <string name="domain_name">Nome do domínio</string>
+    <string name="domain_deleted">Domínio excluído</string>
+    <string name="domain_name_instructions">*. pode ser anexado a um domínio para incluir todos os subdomínios (e.g. *.stoutner.com)</string>
+    <string-array name="font_size_array">
+        <item>Padrão do Sistema</item>
+        <item>Tamanho de fonte personalizado</item>
+    </string-array>
+    <string-array name="swipe_to_refresh_array">
+        <item>Padrão do Sistema</item>
+        <item>Deslize para atualizar ativado</item>
+        <item>Deslize para atualizar desativado</item>
+    </string-array>
+    <string-array name="webview_theme_array">
+        <item>Padrão do Sistema</item>
+        <item>Tema Claro WebView</item>
+        <item>Tema Escuro WebView</item>
+    </string-array>
+    <string-array name="wide_viewport_array">
+        <item>Padrão do Sistema</item>
+        <item>Janela de visualização ampla ativada</item>
+        <item>Janela de visualização ampla desativada</item>
+    </string-array>
+    <string-array name="display_webpage_images_array">
+        <item>Padrão do Sistema</item>
+        <item>Imagens habilitadas</item>
+        <item>Imagens desabilitadas</item>
+    </string-array>
+    <string name="pinned_ssl_certificate">Certificado SSL fixado</string>
+    <string name="saved_ssl_certificate">Certificado SSL salvo</string>
+    <string name="current_website_ssl_certificate">Certificado SSL do site atual</string>
+    <string name="load_an_encrypted_website">Carregue um site criptografado antes de abrir as configurações de domínio para preencher o certificado SSL do site atual.</string>
+    <string name="pinned_ip_addresses">Endereços IP fixados</string>
+    <string name="saved_ip_addresses">Endereços IP salvos</string>
+    <string name="current_ip_addresses">Endereços IP atuais</string>
+
+    <!-- Import/Export. -->
+    <string name="encryption">Encriptação</string>
+    <string-array name="encryption_type">
+        <item>Nenhum</item>
+        <item>Senha</item>
+        <item>OpenPGP</item>
+    </string-array>
+    <string name="kitkat_password_encryption_message">A criptografia de senha não funciona no Android KitKat.</string>
+    <string name="file_does_not_exist">O arquivo não existe.</string>
+    <string name="file_exists_warning">O arquivo já existe. Se você continuar, ele será sobrescrito.</string>
+    <string name="openkeychain_required">A criptografia OpenPGP requer que o OpenKeychain seja instalado.</string>
+    <string name="openkeychain_import_instructions">O arquivo não criptografado terá que ser importado em uma etapa separada após ser descriptografado.</string>
+    <string name="settings_pbs">Settings.pbs</string>
+    <string name="file_location">Localização do Arquivo</string>
+    <string name="browse">Navegar</string>
+    <string name="export">Exportar</string>
+    <string name="import_button">Importar</string>  <!-- `import` is a reserved word and cannot be used as the name. -->
+    <string name="decrypt">Descriptografar</string>
+    <string name="export_successful">Exportação bem sucedida.</string>
+    <string name="export_failed">A exportação falhou:</string>
+    <string name="import_failed">A importação falhou:</string>
+    <string name="storage_permission">Permissão de armazenamento</string>
+    <string name="storage_permission_message">O Privacy Browser precisa de permissão de armazenamento para acessar diretórios públicos.
+        Se for negado, os diretórios do aplicativo ainda podem ser usados.</string>
+    <string name="storage_permission_explanation">O acesso a arquivos em diretórios públicos requer permissão de armazenamento. Caso contrário, apenas os diretórios de aplicativos funcionarão.</string>
+    <string name="cannot_use_location">Este local não pode ser usado porque a permissão de armazenamento não foi concedida.</string>
+
+    <!-- Logcat. -->
+    <string name="copy_string">Cópia</string>  <!-- `copy` is a reserved word and should not be used as the name. -->
+    <string name="logcat_copied">Logcat copiado.</string>
+    <string name="clear">Limpar</string>
+    <string name="save_logcat">Salvar logcat</string>
+    <string name="privacy_browser_logcat_txt">Privacy Browser Logcat.txt</string>
+    <string name="file_saved_successfully">Arquivo salvo com sucesso.</string>
+    <string name="save_failed">Falha ao salvar:</string>
+
+    <!-- Guide. -->
+    <string name="overview">Visão geral</string>
+    <string name="local_storage">Armazenamento Local</string>
+    <string name="ssl_certificates">Certificados SSL</string>
+    <string name="proxies">Proxies</string>
+    <string name="tracking_ids">IDs de rastreamento</string>
+
+    <!-- Proxy. -->
+    <string name="orbot_not_installed_title">Orbot Não  Instalado</string>
+    <string name="orbot_not_installed_message">O proxy através do Orbot não funcionará a menos que o aplicativo Orbot esteja instalado.</string>
+    <string name="i2p_not_installed_title">I2P Não  Instalado</string>
+    <string name="i2p_not_installed_message">O proxy através do I2P não funcionará a menos que o aplicativo I2P esteja instalado.</string>
+    <string name="waiting_for_orbot">Esperando que o Orbot se conecte.</string>
+    <string name="custom_proxy_invalid">O URL do proxy personalizado é inválido.</string>
+    <string name="socks_proxies_do_not_work_on_kitkat">Os proxies SOCKS não funcionam no Android KitKat.</string>
+
+    <!-- About Activity. -->
+    <string name="about_privacy_browser">Sobre o Privacy Browser</string>
+    <string name="version">Versão</string>
+    <string name="version_code">Código da Versão</string>
+    <string name="hardware">Hardware</string>
+    <string name="brand">Marca:</string>
+    <string name="manufacturer">Fabricante:</string>
+    <string name="model">Modelo:</string>
+    <string name="device">Dispositivo:</string>
+    <string name="bootloader">Bootloader:</string>
+    <string name="radio">Radio:</string>
+    <string name="software">Software</string>
+    <string name="android">Android:</string>
+    <string name="api">API</string>
+    <string name="build">Build:</string>
+    <string name="security_patch">Patch de segurança:</string>
+    <string name="webview_provider">Fornecedor WebView:</string>
+    <string name="webview_version">Versão do WebView:</string>
+    <string name="orbot">Orbot:</string>
+    <string name="i2p">I2P:</string>
+    <string name="openkeychain">OpenKeychain:</string>
+    <string name="easylist_label">EasyList:</string>
+    <string name="easyprivacy_label">EasyPrivacy:</string>
+    <string name="fanboy_annoyance_label">Fanboy’s Annoyance List:</string>
+    <string name="fanboy_social_label">Fanboy’s Social Blocking List:</string>
+    <string name="ultralist_label">UltraList:</string>
+    <string name="ultraprivacy_label">UltraPrivacy:</string>
+    <string name="package_signature">Assinatura do Pacote</string>
+    <string name="issuer_dn">DN do emissor:</string>
+    <string name="subject_dn">Assunto DN:</string>
+    <string name="certificate_version">Versão do certificado:</string>
+    <string name="serial_number">Número de série:</string>
+    <string name="signature_algorithm">Algoritmo de Assinatura:</string>
+    <string name="permissions">Permissões</string>
+    <string name="privacy_policy">Política de Privacidade</string>
+    <string name="changelog">Changelog</string>
+    <string name="licenses">Licenças</string>
+    <string name="contributors">Contribuidores</string>
+    <string name="links">Links</string>
+
+    <!-- Preferences. -->
+    <string name="privacy">Privacidade</string>
+    <string name="javascript_preference">JavaScript</string>
+    <string name="javascript_preference_summary">JavaScript permite que sites executem programas (scripts) no dispositivo.</string>
+    <string name="first_party_cookies_preference">Cookies primários</string>
+    <string name="first_party_cookies_preference_summary">Como os cookies primários são uma configuração de nível de aplicativo, quando a guia ativa tem cookies habilitados,
+        todas as solicitações de rede feitas em segundo plano por outras guias também incluirão quaisquer cookies armazenados para seus domínios.
+        O Android KitKat (versão 4.4.x) não diferencia entre cookies primários e de terceiros e os habilitará com esta configuração.</string>
+    <string name="third_party_cookies_preference">Cookies de terceiros</string>
+    <string name="third_party_cookies_summary">Esta configuração requer Android Lollipop (versão 5.0) ou superior. Não tem efeito se os cookies primários estiverem desativados.</string>
+    <string name="dom_storage_preference">Armazenamento DOM</string>
+    <string name="dom_storage_preference_summary">JavaScript deve estar habilitado para que o armazenamento DOM funcione.</string>
+    <string name="save_form_data_preference">Dados do formulário</string>  <!-- The form data strings can be removed once the minimum API >= 26. -->
+    <string name="save_form_data_preference_summary">Dados de formulário salvos podem preencher campos automaticamente em sites.</string>
+        <!-- The form data strings can be removed once the minimum API >= 26. -->
+    <string name="user_agent">Mimetizar o Navegador:</string>
+    <string-array name="translated_user_agent_names">
+        <item>Privacy Browser</item>
+        <item>WebView Padrão</item>
+        <item>Firefox para Android</item>
+        <item>Chrome para Android</item>
+        <item>Safari para iOS</item>
+        <item>Firefox para Linux</item>
+        <item>Chromium para Linux</item>
+        <item>Firefox para Windows</item>
+        <item>Chrome para Windows</item>
+        <item>Edge para Windows</item>
+        <item>Internet Explorer para Windows</item>
+        <item>Safari para macOS</item>
+        <item>Personalizado</item>
+    </string-array>
+    <string-array name="translated_domain_settings_user_agent_names">  <!-- The translated names of the user agents with a System Default option for the domains spinner. -->
+        <item>Padrão do Sistema</item>
+        <item>Privacy Browser</item>
+        <item>WebView Padrão</item>
+        <item>Firefox para Android</item>
+        <item>Chrome para Android</item>
+        <item>Safari para iOS</item>
+        <item>Firefox para Linux</item>
+        <item>Chromium para Linux</item>
+        <item>Firefox para Windows</item>
+        <item>Chrome para Windows</item>
+        <item>Edge para Windows</item>
+        <item>Internet Explorer para Windows</item>
+        <item>Safari para macOS</item>
+        <item>Personalizado</item>
+    </string-array>
+    <string name="custom_user_agent">Agente de usuário personalizado</string>
+    <string name="incognito_mode">Modo de navegação anônima</string>
+    <string name="incognito_mode_summary">Limpe o histórico e o cache após o término do carregamento de cada página da web. No modo de navegação anônima,
+        volta para fechar a guia (ou o aplicativo, se houver apenas uma guia).</string>
+    <string name="do_not_track">Não rastreie</string>
+    <string name="do_not_track_summary">Envie \'Não Rastreie\' ao cabeçalho, que sugere educadamente que os servidores da web não rastreiam este navegador.</string>
+    <string name="allow_screenshots">Permitir capturas de tela</string>
+    <string name="allow_screenshots_summary">Permitir capturas de tela, gravação de vídeo e visualização em monitores não seguros. Alterar esta configuração irá reiniciar o Privacy Browser.</string>
+    <string name="blocklists">Blocklists</string>
+    <string name="easylist">EasyList</string>
+    <string name="easylist_summary">Lista principal de bloqueio de anúncios.</string>
+    <string name="easyprivacy">EasyPrivacy</string>
+    <string name="easyprivacy_summary">Lista de bloqueio do rastreador principal.</string>
+    <string name="fanboys_annoyance_list"> Lista de importunação Fanboy’s</string>
+    <string name="fanboys_annoyance_list_summary">Bloqueie popups e links irritantes. Inclui listas de bloqueio social do Fanboy.</string>
+    <string name="fanboys_social_blocking_list">Lista de bloqueio social do Fanboy</string>
+    <string name="fanboys_social_blocking_list_summary">Bloqueia conteúdo de mídia social de terceiros.</string>
+    <string name="ultralist">UltraList</string>
+    <string name="ultralist_summary">O UltraList bloqueia anúncios que EasyList não bloqueia porque fazer isso pode quebrar sites.</string>
+    <string name="ultraprivacy">UltraPrivacy</string>
+    <string name="ultraprivacy_summary">O UltraPrivacy bloqueia rastreadores que o EasyPrivacy não bloqueia, pois isso pode corromper sites.</string>
+    <string name="block_all_third_party_requests">Bloquear todas as solicitações de terceiros</string>
+    <string name="block_all_third_party_requests_summary">Bloquear todas as solicitações de terceiros aumenta a privacidade, mas quebra muitos sites.</string>
+    <string name="url_modification">Modificação de URL</string>
+    <string name="google_analytics">Google Analytics</string>
+    <string name="google_analytics_summary">Remova utm_ e amp; utm_ e qualquer coisa depois deles dos URLs.</string>
+    <string name="facebook_click_ids">IDs de clique do Facebook</string>
+    <string name="facebook_click_ids_summary">Remover â€œ?fbclid=†, â€œ&amp;fbclid=†, â€œ?fbadid=†, e â€œ&amp;fbadid=† e qualquer coisa depois deles a partir de URLs.</string>
+    <string name="twitter_amp_redirects">Redirecionamentos de AMP do Twitter</string>
+    <string name="twitter_amp_redirects_summary">Remover â€œ?amp=1† e qualquer coisa depois de URLs.</string>
+    <string name="search">Search</string>
+    <string-array name="search_entries">
+        <item>Startpage</item>
+        <item>Mojeek</item>
+        <item>DuckDuckGo - JavaScript desativado</item>
+        <item>DuckDuckGo - JavaScript ativado</item>
+        <item>Google</item>
+        <item>Bing</item>
+        <item>Yahoo - JavaScript desativado</item>
+        <item>Yahoo - JavaScript ativado</item>
+        <item>Custom</item>
+    </string-array>
+    <string name="custom_url">URL personalizado</string>
+    <string name="search_custom_url">URL personalizado de pesquisa</string>
+    <string name="proxy">Proxy</string>
+    <string name="proxy_none">Nenhum</string>
+    <string name="proxy_tor">Tor</string>
+    <string name="proxy_i2p">I2P</string>
+    <string name="proxy_custom">Personalizado</string>
+    <string-array name="proxy_entries">
+        <item>Nenhum</item>
+        <item>Tor</item>
+        <item>I2P</item>
+        <item>Personalizado</item>
+    </string-array>
+    <string name="no_proxy_enabled">Nenhum - conecte-se diretamente à Internet.</string>
+    <string name="tor_enabled">Tor - conecte-se por meio do socks://localhost:9050.</string>
+    <string name="tor_enabled_kitkat">Tor - conecte-se por meio de http://localhost:8118.</string>
+    <string name="i2p_enabled">I2P - conecte-se por meio de http://localhost:4444.</string>
+    <string name="custom_proxy">Personalizar proxy</string>
+    <string name="proxy_custom_url">URL de proxy personalizado</string>
+    <string name="full_screen">Tela Cheia</string>
+    <string name="full_screen_browsing_mode">Modo de navegação em tela inteira</string>
+    <string name="full_screen_browsing_mode_summary">Toque duas vezes para alternar o modo de navegação em tela inteira.</string>
+    <string name="hide_app_bar">Ocultar a barra de aplicativos</string>
+    <string name="hide_app_bar_summary">Oculte a barra de aplicativos que contém o URL.</string>
+    <string name="clear_everything">Limpar tudo</string>
+    <!-- The form data part of this string can be removed once the minimum API >= 26. -->
+    <string name="clear_everything_summary">Limpa cookies, armazenamento DOM, dados de formulário e cache do WebView. Em seguida, exclui manualmente todos os diretórios “app_webview” e “cache”.</string>
+    <string name="clear_cookies_preference">Limpar cookies</string>
+    <string name="clear_cookies_summary">Limpa os cookies originais e de terceiros.</string>
+    <string name="clear_dom_storage_preference">Limpar armazenamento DOM</string>
+    <string name="clear_dom_storage_summary">Limpa o armazenamento DOM.</string>
+    <string name="clear_form_data_preference">Limpar dados do formulário</string>  <!-- The form data strings can be removed once the minimum API >= 26. -->
+    <string name="clear_form_data_summary">Limpa os dados do formulário.</string>  <!-- The form data strings can be removed once the minimum API >= 26. -->
+    <string name="clear_cache">Limpar cache</string>
+    <string name="clear_cache_summary">Limpa o cache do WebView.</string>
+    <string name="general">Geral</string>
+    <string name="homepage">Pagina inicial</string>
+    <string name="download_location">Localização de Download</string>
+    <string-array name="download_location_entries">
+        <item>Automático</item>
+        <item>Diretório do aplicativo</item>
+        <item>Diretório público</item>
+        <item>Personalizado</item>
+    </string-array>
+    <string name="download_custom_location">Localização de Download Personalizada</string>
+    <string name="font_size_preference">Tamanho da Fonte</string>
+    <string name="open_intents_in_new_tab">Abrir conteúdo em uma nova guia</string>
+    <string name="open_intents_in_new_tab_summary">Conteúdos são links enviados de outros aplicativos.</string>
+    <string name="swipe_to_refresh">Deslize para atualizar</string>
+    <string name="swipe_to_refresh_summary">Alguns sites não funcionam bem se deslizar para atualizar estiver habilitado.</string>
+    <string name="scroll_app_bar">Role a barra de aplicativos</string>
+    <string name="scroll_app_bar_summary">Role a barra de aplicativos para fora da parte superior da tela quando o WebView rola para baixo.</string>
+    <string name="display_additional_app_bar_icons">Exibir ícones adicionais da barra de aplicativos</string>
+    <string name="display_additional_app_bar_icons_summary">Exibe ícones na barra de aplicativos para atualizar o WebView e, se houver espaço, para alternar cookies e armazenamento DOM.</string>
+    <string name="app_theme">Tema do  aplicativo</string>
+    <string-array name="app_theme_entries">
+        <item>Padrão do Sitema</item>
+        <item>Claro</item>
+        <item>Escuro</item>
+    </string-array>
+    <string name="webview_theme">Tema do WebView</string>
+    <string-array name="webview_theme_entries">
+        <item>Padrão do Sitema</item>
+        <item>Claro</item>
+        <item>Escuro</item>
+    </string-array>
+    <string name="wide_viewport_preference">Janela de visualização ampla</string>
+    <string name="wide_viewport_summary">Usar uma janela de visualização ampla torna o layout de algumas páginas da web mais parecido com o site para desktop.</string>
+    <string name="display_webpage_images">Exibir imagens da página da web</string>
+    <string name="display_webpage_images_summary">Desative para conservar a largura de banda.</string>
+
+    <!-- Ad Control. There are no ads in the standard flavor, but these strings must exist because they are referenced in the code. -->
+    <string name="ad_consent">Consentimento de Anúncio</string>
+</resources>
\ No newline at end of file
index 73fc0eff874122ab9c94e158cac7bae387823548..c66123107f484502b0a51a2d447ac5fe2d2ab83c 100644 (file)
         <string name="display_webpage_images">Display webpage images</string>
         <string name="display_webpage_images_summary">Disable to conserve bandwidth.</string>
 
+    <!-- Non-translatable preference keys. -->
+    <string name="allow_screenshots_key" translatable="false">allow_screenshots</string>
+
     <!-- Non-translatable preference default values. -->
     <string name="user_agent_default_value" translatable="false">Privacy Browser</string>
     <string name="custom_user_agent_default_value" translatable="false">PrivacyBrowser/1.0</string>
index a2368ae18f3d302029377ad040d182f4fc8ed30a..cbb381a78b46d9f2ebf788185ff9835abb710b29 100644 (file)
@@ -26,7 +26,7 @@ buildscript {
     }
     dependencies {
         classpath 'com.android.tools.build:gradle:4.0.1'
-        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72"
+        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.10"
 
         // NOTE: Do not place your application dependencies here; they belong
         // in the individual module build.gradle files