Fix View Source crashing on release builds. https://redmine.stoutner.com/issues/642
authorSoren Stoutner <soren@stoutner.com>
Tue, 24 Nov 2020 08:09:35 +0000 (01:09 -0700)
committerSoren Stoutner <soren@stoutner.com>
Tue, 24 Nov 2020 08:12:53 +0000 (01:12 -0700)
33 files changed:
app/build.gradle
app/proguard-rules.pro
app/src/free/assets/pt-rBR/about_permissions.html [new file with mode: 0644]
app/src/free/assets/pt-rBR/about_privacy_policy.html [new file with mode: 0644]
app/src/free/res/values-pt-rBR/strings.xml
app/src/main/assets/de/about_changelog.html
app/src/main/assets/en/about_changelog.html
app/src/main/assets/es/about_changelog.html
app/src/main/assets/fr/about_changelog.html
app/src/main/assets/it/about_changelog.html
app/src/main/assets/pt-rBR/about_changelog.html
app/src/main/assets/pt-rBR/guide_domain_settings.html
app/src/main/assets/pt-rBR/guide_javascript.html
app/src/main/assets/pt-rBR/guide_local_storage.html
app/src/main/assets/pt-rBR/guide_overview.html
app/src/main/assets/pt-rBR/guide_proxies.html
app/src/main/assets/pt-rBR/guide_requests.html
app/src/main/assets/pt-rBR/guide_ssl_certificates.html
app/src/main/assets/pt-rBR/guide_tracking_ids.html
app/src/main/assets/pt-rBR/guide_user_agent.html
app/src/main/assets/ru/about_changelog.html
app/src/main/assets/tr/about_changelog.html
app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java
app/src/main/java/com/stoutner/privacybrowser/activities/ViewSourceActivity.java [deleted file]
app/src/main/java/com/stoutner/privacybrowser/activities/ViewSourceActivity.kt [new file with mode: 0644]
app/src/main/java/com/stoutner/privacybrowser/backgroundtasks/GetSourceBackgroundTask.java
app/src/main/java/com/stoutner/privacybrowser/viewmodelfactories/WebViewSourceFactory.kt
app/src/main/java/com/stoutner/privacybrowser/viewmodels/WebViewSource.java [deleted file]
app/src/main/java/com/stoutner/privacybrowser/viewmodels/WebViewSource.kt [new file with mode: 0644]
app/src/main/res/values/strings.xml
build.gradle
fastlane/metadata/android/pt-rBR/changelogs/52.txt
gradle.properties

index 9f0724550b812349606c354601ae1d517d83d7af..420de25234fdaf6a11f1abd7e5ebe3220a781fb9 100644 (file)
@@ -19,7 +19,6 @@
 
 apply plugin: 'com.android.application'
 apply plugin: 'kotlin-android'
 
 apply plugin: 'com.android.application'
 apply plugin: 'kotlin-android'
-apply plugin: 'kotlin-android-extensions'
 
 android {
     compileSdkVersion 29
 
 android {
     compileSdkVersion 29
@@ -44,7 +43,7 @@ android {
         release {
             minifyEnabled true
             shrinkResources true
         release {
             minifyEnabled true
             shrinkResources true
-            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
         }
     }
 
         }
     }
 
@@ -90,7 +89,7 @@ dependencies {
     implementation 'androidx.webkit:webkit:1.3.0'
 
     // Include the Kotlin standard libraries.  This should be the same version number listed in project build.gradle.
     implementation 'androidx.webkit:webkit:1.3.0'
 
     // 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"
+    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.20"
 
     // Include the Google material library.
     implementation 'com.google.android.material:material:1.2.1'
 
     // Include the Google material library.
     implementation 'com.google.android.material:material:1.2.1'
index 189b58019d321a17467f8fd65e7f496da905eef5..8d07177010e93ad8884b1529996023454ef7b2d1 100644 (file)
@@ -1,6 +1,23 @@
+# Copyright © 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/>.
+
 # Add project specific ProGuard rules here.
 # By default, the flags in this file are appended to flags specified
 # Add project specific ProGuard rules here.
 # By default, the flags in this file are appended to flags specified
-# in /home/soren/Android/Sdk/tools/proguard/proguard-android.txt
+# in ~/Android/Sdk/tools/proguard/proguard-android-optimize.txt
 # You can edit the include path and order by changing the proguardFiles
 # directive in build.gradle.
 #
 # You can edit the include path and order by changing the proguardFiles
 # directive in build.gradle.
 #
@@ -15,3 +32,6 @@
 #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
 #   public *;
 #}
 #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
 #   public *;
 #}
+
+# Make Kotlin ViewModels work correctly.
+-keep class * extends androidx.lifecycle.ViewModel { *; }
\ No newline at end of file
diff --git a/app/src/free/assets/pt-rBR/about_permissions.html b/app/src/free/assets/pt-rBR/about_permissions.html
new file mode 100644 (file)
index 0000000..f3c3639
--- /dev/null
@@ -0,0 +1,72 @@
+<!--
+  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/>. -->
+
+<html>
+    <head>
+        <meta charset="UTF-8">
+
+        <link rel="stylesheet" href="../css/theme.css">
+
+        <!-- Setting the color scheme instructs the WebView to respect `prefers-color-scheme` @media CSS. -->
+        <meta name="color-scheme" content="light dark">
+    </head>
+
+    <body>
+        <h3>Have full network access</h3>
+        <p><a href="https://developer.android.com/reference/android/Manifest.permission.html#INTERNET">android.permission.INTERNET</a></p>
+        <p>Required for the WebView to access the internet. Without this permission, Privacy Browser would be “No Browser: Protecting Your Privacy by Staying Completely Off the Internet”.</p>
+
+        <h3>Install shortcuts</h3>
+        <p><a href="https://developer.android.com/reference/android/Manifest.permission.html#INSTALL_SHORTCUT">com.android.launcher.permission.INSTALL_SHORTCUT</a></p>
+        <p>Required to add shortcuts for websites to the launcher desktop.</p>
+
+        <h3>Read storage</h3>
+        <p><a href="https://developer.android.com/reference/android/Manifest.permission#READ_EXTERNAL_STORAGE">android.permission.READ_EXTERNAL_STORAGE</a></p>
+        <p>Required to import settings from public folders. On Android Marshmallow (API 23) and newer, if this permission is denied Privacy Browser can import settings from the app’s folders instead.</p>
+
+        <h3>Write storage</h3>
+        <p><a href="https://developer.android.com/reference/android/Manifest.permission.html#WRITE_EXTERNAL_STORAGE">android.permission.WRITE_EXTERNAL_STORAGE</a></p>
+        <p>Required to export settings and download files to the public folders.
+            On Android Marshmallow (API 23) and newer, if this permission is denied Privacy Browser can export settings and store downloads in the app’s folders instead.</p>
+
+        <br/>
+        <hr/>
+        <br/>
+
+        <p>In addition, Privacy Browser Free displays ads from Google’s AdMob network using the Firebase backend.
+            For the free flavor, Firebase adds the following permissions even though they are not listed in the source code
+            <a href="https://git.stoutner.com/?p=PrivacyBrowser.git;a=blob;f=app/src/main/AndroidManifest.xml;hb=HEAD">manifest file</a>.</p>
+
+        <h3>View network connections</h3>
+        <p><a href="https://developer.android.com/reference/android/Manifest.permission.html#ACCESS_NETWORK_STATE">android.permission.ACCESS_NETWORK_STATE</a></p>
+        <p>Allows the ads to tell when you are connected to the internet and when you aren’t (presumably so they don’t try to reload an ad when you are disconnected).
+            They can also tell if you are connected via Wi-Fi, 2G, 3G, 4G, etc.</p>
+
+        <h3>Prevent phone from sleeping</h3>
+        <p><a href="https://developer.android.com/reference/android/Manifest.permission.html#WAKE_LOCK">android.permission.WAKE_LOCK</a></p>
+        <p>Allows the ads to keep the processor from sleeping and the screen from dimming, although in my testing I don’t think the ads actually do this.</p>
+
+        <h3>Play Install Referrer API</h3>
+        <p><a href="https://android-developers.googleblog.com/2017/11/google-play-referrer-api-track-and.html">com.google.android.finsky.permission.BIND_GET_INSTALL_REFERRER_SERVICE</a></p>
+        <p>Allows other apps to tell if their installation was launched from an ad in Privacy Browser Free.</p>
+
+        <h3>Receive data from Internet</h3>
+        <p><a href="http://androidpermissions.com/permission/com.google.android.c2dm.permission.RECEIVE">com.google.android.c2dm.permission.RECEIVE</a></p>
+        <p>Allows Google to send information directly to the AdView without having to receive a request first (cloud-to-device messaging).</p>
+    </body>
+</html>
\ No newline at end of file
diff --git a/app/src/free/assets/pt-rBR/about_privacy_policy.html b/app/src/free/assets/pt-rBR/about_privacy_policy.html
new file mode 100644 (file)
index 0000000..0ec033a
--- /dev/null
@@ -0,0 +1,106 @@
+<!--
+  Copyright © 2016-2018,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/>. -->
+
+<html>
+    <head>
+        <meta charset="UTF-8">
+
+        <link rel="stylesheet" href="../css/theme.css">
+
+        <!-- Setting the color scheme instructs the WebView to respect `prefers-color-scheme` @media CSS. -->
+        <meta name="color-scheme" content="light dark">
+    </head>
+
+    <body>
+        <h3>Privacy Browser Free</h3>
+        <p><strong class="red">Privacy Browser Free does not collect any user information.</strong></p>
+
+
+        <h3>Google Play</h3>
+        <p>Google Play has its <a href="https://www.google.com/intl/en/policies/privacy/">own privacy policy</a>.
+            Google provides <em>anonymized summary installation information</em> to developers, including the number of installs organized by the following categories.</p>
+        <ul>
+            <li><item>Android version</item> (eg. Android 7.1)</li>
+            <li><item>Device</item> (eg. Samsung Galaxy S6 [zeroflte])</li>
+            <li><item>Tablets</item> (eg. Tablets 10" and above)</li>
+            <li><item>Country</item> (eg. United States)</li>
+            <li><item>Language</item> (eg. English [United States])</li>
+            <li><item>App version</item> (eg. 14)</li>
+            <li><item>Carrier</item> (eg. T-Mobile - US)</li>
+        </ul>
+
+
+        <h3>Google Play Ratings</h3>
+        <p>Google Play has its <a href="https://www.google.com/intl/en/policies/privacy/">own privacy policy</a>.
+            Google provides developers with <em>anonymized summaries</em> of the following information related to user ratings.</p>
+        <ul>
+            <li><item>Country</item> (eg. United States)</li>
+            <li><item>Language</item> (eg. English)</li>
+            <li><item>App version</item> (eg. 14)</li>
+            <li><item>Android version</item> (eg. Android 7.1)</li>
+            <li><item>Device</item> (eg. Google Nexus 5X [bullhead])</li>
+            <li><item>Tablets</item> (eg. Tablets 10" and above)</li>
+        </ul>
+
+
+        <h3>Google Play Reviews</h3>
+        <p>Google Play has its <a href="https://www.google.com/intl/en/policies/privacy/">own privacy policy</a>.
+            In addition to the name of the reviewer, the rating, and the text of the review (which are all available publicly), Google provides some or all of the following information to the developer.</p>
+        <ul>
+            <li><item>Version code</item> (eg. 7)</li>
+            <li><item>Version name</item> (eg. 1.6)</li>
+            <li><item>Android version</item> (eg. Android 5.1)</li>
+            <li><item>Device</item> (eg. Galaxy S6 Edge+ [zenlte])</li>
+            <li><item>Manufacturer</item> (eg. Samsung)</li>
+            <li><item>Device type</item> (eg. Phone)</li>
+            <li><item>CPU make</item> (eg. Samsung)</li>
+            <li><item>CPU model</item> (eg. Exynos 7420)</li>
+            <li><item>Screen density</item> (eg. 560 dpi)</li>
+            <li><item>Screen size</item> (eg. 2560 x 1440)</li>
+            <li><item>RAM</item> (eg. 4096 MB)</li>
+            <li><item>Native platform</item> (eg. armeabi-v7a,armeabi,arm64v8a)</li>
+            <li><item>OpenGL ES version</item> (eg. 3.1)</li>
+            <li><item>Device language</item> (eg. English)</li>
+        </ul>
+
+
+        <h3>Advertisements</h3>
+        <p>Privacy Browser Free displays a banner ad across the bottom of the screen using Google's AdMob network,
+            which has its <a href="https://www.google.com/intl/en/policies/privacy/">own privacy policy</a>.
+            These ads are set to be <a href="https://developers.google.com/admob/android/eu-consent#update_consent_status">non-personalized</a>.
+            AdMob reports <em>anonymized summaries</em> of the following information to developers.</p>
+        <ul>
+            <li><item>Total impressions</item></li>
+            <li><item>Total clicks</item></li>
+            <li><item>Platforms</item> (eg. high-end mobile devices, tablets)</li>
+            <li><item>Activity by country</item></li>
+        </ul>
+
+
+        <h3>Direct Communications</h3>
+        <p>Users may choose to send direct communications to Stoutner, like email messages and comments on <a href="https://www.stoutner.com/">stoutner.com</a>.</p>
+
+
+        <h3>Use of Information</h3>
+        <p><strong class="blue">Stoutner may use this information to assist in the development of Privacy Browser and communicate the status of the project to users.</strong>
+            <strong class="red">Stoutner will never sell this information nor transfer it to any third party that would use it for advertising or marketing.</strong></p>
+
+        <hr />
+        <p style="text-align: center;"><em>Revision 1.7, 14 May 2019</em></p>
+    </body>
+</html>
\ No newline at end of file
index af5696bc62612233f1be4accf993f827340625bf..63aaceb80880114be45ed1fbfd81210e36028108 100644 (file)
 
 <resources>
     <!-- Activities. -->
 
 <resources>
     <!-- Activities. -->
+    <string name="privacy_browser">Privacy Browser Gratuito</string>
 
     <!-- Create Home Screen Shortcut Alert Dialog. -->
 
     <!-- Create Home Screen Shortcut Alert Dialog. -->
+    <string name="open_with_privacy_browser">Abrir com Privacy Browser Gratuito.</string>
 
     <!-- Ad Consent. -->
 
     <!-- Ad Consent. -->
+    <string name="ad_consent_text">Privacy Browser Gratuito exibe um anúncio de banner na parte inferior da tela.
+        Esses anúncios vêm do conjunto de provedores comumente usados do Google e são configurados para não serem personalizados.
+        \n\nA versão padrão do Privacy Browser não contém anúncios de aplicativos.</string>
+    <string name="close_browser">Fechar Navegador</string>
+    <string name="accept_ads">Aceitar Anúncios</string>
 </resources>
\ No newline at end of file
 </resources>
\ No newline at end of file
index b792598c12126820d445f4d8689d0bc359ae5e9e..d085d3414a011f588dd236dd58122825fa91dab6 100644 (file)
@@ -33,8 +33,8 @@
     </head>
 
     <body>
     </head>
 
     <body>
-        <h3>3.6 (version code 52)</h3>
-        <p>16. November 2020 - Mindest-API 19, Ziel-API 29</p>
+        <h3><a href="https://www.stoutner.com/privacy-browser-3-6/">3.6</a> (version code 52)</h3>
+        <p><a href="https://git.stoutner.com/?p=PrivacyBrowser.git;a=commitdiff;h=8a06558b0071fa94e2a7d1093b3118417ac5cc8f">16. November 2020</a> - Mindest-API 19, Ziel-API 29</p>
         <ul>
             <li>Fix eines Bugs beim gepufferten Abspielen von Audio <a href="https://redmine.stoutner.com/issues/595">nach dem Schließen eines Tabs</a>.</li>
             <li>Möglichkeit, unter Android 7 (API 24) und neuer <a href="https://redmine.stoutner.com/issues/636">benutzerdfinierten Zertifikatsstellen zu vertrauen</a>.</li>
         <ul>
             <li>Fix eines Bugs beim gepufferten Abspielen von Audio <a href="https://redmine.stoutner.com/issues/595">nach dem Schließen eines Tabs</a>.</li>
             <li>Möglichkeit, unter Android 7 (API 24) und neuer <a href="https://redmine.stoutner.com/issues/636">benutzerdfinierten Zertifikatsstellen zu vertrauen</a>.</li>
index 7698b523e39dba007e09af1d5ad9fd011053938e..04f855fb2eb7fdd35a569b2addae3cf171b9fae6 100644 (file)
@@ -27,8 +27,8 @@
     </head>
 
     <body>
     </head>
 
     <body>
-        <h3>3.6 (version code 52)</h3>
-        <p>16 November 2020 - minimum API 19, target API 29</p>
+        <h3><a href="https://www.stoutner.com/privacy-browser-3-6/">3.6</a> (version code 52)</h3>
+        <p><a href="https://git.stoutner.com/?p=PrivacyBrowser.git;a=commitdiff;h=8a06558b0071fa94e2a7d1093b3118417ac5cc8f">16 November 2020</a> - minimum API 19, target API 29</p>
         <ul>
             <li>Fix buffered audio playing <a href="https://redmine.stoutner.com/issues/595">after a tab is closed</a>.</li>
             <li>Trust <a href="https://redmine.stoutner.com/issues/636">user certificate authorities</a> on Android 7 (API 24) and newer.</li>
         <ul>
             <li>Fix buffered audio playing <a href="https://redmine.stoutner.com/issues/595">after a tab is closed</a>.</li>
             <li>Trust <a href="https://redmine.stoutner.com/issues/636">user certificate authorities</a> on Android 7 (API 24) and newer.</li>
index 83ed243155f2193bb64f2ba57f8c2d9cdd637942..e88428edfb60e1fb67a23509377d31adcaa115b4 100644 (file)
@@ -29,8 +29,8 @@
     </head>
 
     <body>
     </head>
 
     <body>
-        <h3>3.6 (código de versión 52)</h3>
-        <p>16 de noviembre de 2020 - API mínimo 19, API dirigido 29</p>
+        <h3><a href="https://www.stoutner.com/privacy-browser-3-6/">3.6</a> (código de versión 52)</h3>
+        <p><a href="https://git.stoutner.com/?p=PrivacyBrowser.git;a=commitdiff;h=8a06558b0071fa94e2a7d1093b3118417ac5cc8f">16 de noviembre de 2020</a> - API mínimo 19, API dirigido 29</p>
         <ul>
             <li>Arreglar la reproducción de audio con buffer <a href="https://redmine.stoutner.com/issues/595">después de cerrar una pestaña</a>.</li>
             <li>Confiar en <a href="https://redmine.stoutner.com/issues/636">las autoridades de certificación de usuarios</a> de Android 7 (API 24) o más reciente.</li>
         <ul>
             <li>Arreglar la reproducción de audio con buffer <a href="https://redmine.stoutner.com/issues/595">después de cerrar una pestaña</a>.</li>
             <li>Confiar en <a href="https://redmine.stoutner.com/issues/636">las autoridades de certificación de usuarios</a> de Android 7 (API 24) o más reciente.</li>
index 69d5cef869baae3d4f50f88ad99f2e82a5306575..50809252e47580ff3534118ed48ade7f52ccbaa8 100644 (file)
@@ -29,8 +29,8 @@
     </head>
 
     <body>
     </head>
 
     <body>
-        <h3>3.6 (version du code 52)</h3>
-        <p>16 Novembre 2020 - API minimale : 19, API optimale : 29</p>
+        <h3><a href="https://www.stoutner.com/privacy-browser-3-6/">3.6</a> (version du code 52)</h3>
+        <p><a href="https://git.stoutner.com/?p=PrivacyBrowser.git;a=commitdiff;h=8a06558b0071fa94e2a7d1093b3118417ac5cc8f">16 Novembre 2020</a> - API minimale : 19, API optimale : 29</p>
         <ul>
             <li>Correction de la lecture audio en mémoire tampon <a href="https://redmine.stoutner.com/issues/595">après la fermeture d'un onglet</a>.</li>
             <li>Confiance aux <a href="https://redmine.stoutner.com/issues/636">autorités de certification de l'utilisateur</a> sur Android 7 (API 24) et suivants.</li>
         <ul>
             <li>Correction de la lecture audio en mémoire tampon <a href="https://redmine.stoutner.com/issues/595">après la fermeture d'un onglet</a>.</li>
             <li>Confiance aux <a href="https://redmine.stoutner.com/issues/636">autorités de certification de l'utilisateur</a> sur Android 7 (API 24) et suivants.</li>
index 51f69f8323ff0f409e12fe14c9e9f625591c3fba..b3dd895c420eee22b1137d5db0a233536f3da263 100644 (file)
@@ -29,8 +29,8 @@
     </head>
 
     <body>
     </head>
 
     <body>
-        <h3>3.6 (versione codice 52)</h3>
-        <p>16 Novembre 2020 - minima API 19, target API 29</p>
+        <h3><a href="https://www.stoutner.com/privacy-browser-3-6/">3.6</a> (versione codice 52)</h3>
+        <p><a href="https://git.stoutner.com/?p=PrivacyBrowser.git;a=commitdiff;h=8a06558b0071fa94e2a7d1093b3118417ac5cc8f">16 Novembre 2020</a> - minima API 19, target API 29</p>
         <ul>
             <li>Sistemazione della riproduzione audio nel buffer <a href="https://redmine.stoutner.com/issues/595">dopo la chiusura di una scheda</a>.</li>
             <li>Accettazione delle <a href="https://redmine.stoutner.com/issues/636">autorità dei certificati degli utenti</a> su Android 7 (API 24) e successivi.</li>
         <ul>
             <li>Sistemazione della riproduzione audio nel buffer <a href="https://redmine.stoutner.com/issues/595">dopo la chiusura di una scheda</a>.</li>
             <li>Accettazione delle <a href="https://redmine.stoutner.com/issues/636">autorità dei certificati degli utenti</a> su Android 7 (API 24) e successivi.</li>
index 8576c83e6c360462f7c9c8f355288cfdd066a64b..b58d513f83121b9efa57e3b49565368913c5a962 100644 (file)
@@ -29,8 +29,8 @@
     </head>
 
     <body>
     </head>
 
     <body>
-        <h3>3.6 (código da versão 52)</h3>
-        <p>16 November 2020 - minimum API 19, target API 29</p>
+        <h3><a href="https://www.stoutner.com/privacy-browser-3-6/">3.6</a> (código da versão 52)</h3>
+        <p><a href="https://git.stoutner.com/?p=PrivacyBrowser.git;a=commitdiff;h=8a06558b0071fa94e2a7d1093b3118417ac5cc8f">16 November 2020</a> - minimum API 19, target API 29</p>
         <ul>
             <li>Correção da reprodução do áudio em buffer <a href="https://redmine.stoutner.com/issues/595">após o fechamento de uma guia</a>.</li>
             <li>Confiar <a href="https://redmine.stoutner.com/issues/636">nas autoridades de certificação do usuário</a> no Android 7 (API 24) e mais recente.</li>
         <ul>
             <li>Correção da reprodução do áudio em buffer <a href="https://redmine.stoutner.com/issues/595">após o fechamento de uma guia</a>.</li>
             <li>Confiar <a href="https://redmine.stoutner.com/issues/636">nas autoridades de certificação do usuário</a> no Android 7 (API 24) e mais recente.</li>
                 <a href="https://redmine.stoutner.com/issues/618">user</a> <a href="https://redmine.stoutner.com/issues/609">experiência</a> <a href="https://redmine.stoutner.com/issues/592">e</a>
                 <a href="https://redmine.stoutner.com/issues/567">gráfica</a> <a href="https://redmine.stoutner.com/issues/554">interface</a>.</li>
             <li>Tradução do português brasileiro fornecida por <a href="mailto:mochileiro2006-trilhas@yahoo.com.br">Thiago Nazareno Conceição Silva de Jesus</a>.</li>
                 <a href="https://redmine.stoutner.com/issues/618">user</a> <a href="https://redmine.stoutner.com/issues/609">experiência</a> <a href="https://redmine.stoutner.com/issues/592">e</a>
                 <a href="https://redmine.stoutner.com/issues/567">gráfica</a> <a href="https://redmine.stoutner.com/issues/554">interface</a>.</li>
             <li>Tradução do português brasileiro fornecida por <a href="mailto:mochileiro2006-trilhas@yahoo.com.br">Thiago Nazareno Conceição Silva de Jesus</a>.</li>
-            <li>Updated French translation provided by <a href="mailto:kevinliste@framalistes.org">Kévin LE FLOHIC</a>.</li>
-            <li>Updated German translation provided by Bernhard G. Keller.</li>
-            <li>Updated Italian translation provided by Francesco Buratti.</li>
-            <li>Updated Russian translation.</li>
-            <li>Updated Spanish translation provided by Jose A. León.</li>
+            <li>Tradução francesa atualizada fornecida por <a href="mailto:kevinliste@framalistes.org">Kévin LE FLOHIC</a>.</li>
+            <li>Tradução alemã atualizada fornecida por Bernhard G. Keller.</li>
+            <li>Tradução italiana atualizada fornecida por Francesco Buratti.</li>
+            <li>Tradução russa atualizada.</li>
+            <li>Tradução em espanhol atualizada fornecida por Jose A. León.</li>
         </ul>
 
         <h3><a href="https://www.stoutner.com/privacy-browser-3-5-1/">3.5.1</a> (código da versão 51)</h3>
         </ul>
 
         <h3><a href="https://www.stoutner.com/privacy-browser-3-5-1/">3.5.1</a> (código da versão 51)</h3>
index 28d90e2b2f323bf9d356eff834b7aac2068444f2..be8e2b8d132876e29eb2110386d0b0124d5dbf62 100644 (file)
@@ -1,6 +1,8 @@
 <!--
   Copyright © 2017-2020 Soren Stoutner <soren@stoutner.com>.
 
 <!--
   Copyright © 2017-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
   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
index 419dd0b2831603eac3e4cda56bfd61b2f37d6b90..a6566a01c5980ec60204d2abc19e1b5843050557 100644 (file)
@@ -1,6 +1,8 @@
 <!--
   Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
 
 <!--
   Copyright © 2016-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
   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
index dcc22ebacc6dd52fafeca77a15d07ee973a2c50d..bb429d1975e1f3bc33aa9ded7b15a5f5164dbdfa 100644 (file)
@@ -1,6 +1,8 @@
 <!--
   Copyright © 2016-2018,2020 Soren Stoutner <soren@stoutner.com>.
 
 <!--
   Copyright © 2016-2018,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
   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
index 4cda1de328dc13cea1e098e9ec8498e090fb9a9f..5d7183b7f2391acf09f2c9da10803f1e9508cde5 100644 (file)
@@ -1,6 +1,8 @@
 <!--
   Copyright © 2016-2018,2020 Soren Stoutner <soren@stoutner.com>.
 
 <!--
   Copyright © 2016-2018,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
   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
index 31d810dcae1e453b3bf53073daffc94ae6a52ed5..abfdd2e294d6ea884dd90eea30c6cdb3ebc25796 100644 (file)
@@ -1,6 +1,8 @@
 <!--
   Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
 
 <!--
   Copyright © 2016-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
   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
index bfd6d8fae5794db423f847eec38fa3a1c4401fc5..facaa9db36aeaa491e914f6512ac015c986d9e99 100644 (file)
@@ -1,6 +1,8 @@
 <!--
   Copyright © 2018,2020 Soren Stoutner <soren@stoutner.com>.
 
 <!--
   Copyright © 2018,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
   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
index d0eea92c13bc764fe30fd593f638f16c3d5f4ba5..ac9fafc6820d8f9d27bdabce5f85b9f37202fa60 100644 (file)
@@ -1,6 +1,8 @@
 <!--
   Copyright © 2017-2020 Soren Stoutner <soren@stoutner.com>.
 
 <!--
   Copyright © 2017-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
   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
index ce93eca91173e54e3762796f95647395620d9b70..1973b7208ab60bd696ffdbaea6fb229092cda449 100644 (file)
@@ -1,6 +1,8 @@
 <!--
   Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
 
 <!--
   Copyright © 2016-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
   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
index 6f8feb5ea9e2a971cf874cdf40e06898b8aadf2e..be5b017ebb61d907775cfd23335ae644b76b4198 100644 (file)
@@ -1,6 +1,8 @@
 <!--
   Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
 
 <!--
   Copyright © 2016-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
   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
index 55c3d011e8f8bb0d679b014c21b00588633c6fdf..2e64e6740af44f194fcb6ce93f53920b9dec3c7b 100644 (file)
@@ -27,8 +27,8 @@
     </head>
 
     <body>
     </head>
 
     <body>
-        <h3>3.6 (код версии 52)</h3>
-        <p>16 Ноябрь 2020 года - минимальный API 19, целевой API 29</p>
+        <h3><a href="https://www.stoutner.com/privacy-browser-3-6/">3.6</a> (код версии 52)</h3>
+        <p><a href="https://git.stoutner.com/?p=PrivacyBrowser.git;a=commitdiff;h=8a06558b0071fa94e2a7d1093b3118417ac5cc8f">16 Ноябрь 2020 года</a> - минимальный API 19, целевой API 29</p>
         <ul>
             <li>Исправлено воспроизведение буферизованного звука <a href="https://redmine.stoutner.com/issues/595">после закрытия вкладки</a>.</li>
             <li>Доверие <a href="https://redmine.stoutner.com/issues/636">пользовательским сертификатам авторизации</a> на Android 7 (API 24) и новее.</li>
         <ul>
             <li>Исправлено воспроизведение буферизованного звука <a href="https://redmine.stoutner.com/issues/595">после закрытия вкладки</a>.</li>
             <li>Доверие <a href="https://redmine.stoutner.com/issues/636">пользовательским сертификатам авторизации</a> на Android 7 (API 24) и новее.</li>
index 9ad5d44c2da8c05779265d6f58f9e34c38d00b40..3ecb5bca3eb0647d3d3db83a3c24205c2bd421a8 100644 (file)
@@ -27,8 +27,8 @@
     </head>
 
     <body>
     </head>
 
     <body>
-        <h3>3.6 (version code 52)</h3>
-        <p>16 November 2020 - minimum API 19, target API 29</p>
+        <h3><a href="https://www.stoutner.com/privacy-browser-3-6/">3.6</a> (version code 52)</h3>
+        <p><a href="https://git.stoutner.com/?p=PrivacyBrowser.git;a=commitdiff;h=8a06558b0071fa94e2a7d1093b3118417ac5cc8f">16 November 2020</a> - minimum API 19, target API 29</p>
         <ul>
             <li>Fix buffered audio playing <a href="https://redmine.stoutner.com/issues/595">after a tab is closed</a>.</li>
             <li>Trust <a href="https://redmine.stoutner.com/issues/636">user certificate authorities</a> on Android 7 (API 24) and newer.</li>
         <ul>
             <li>Fix buffered audio playing <a href="https://redmine.stoutner.com/issues/595">after a tab is closed</a>.</li>
             <li>Trust <a href="https://redmine.stoutner.com/issues/636">user certificate authorities</a> on Android 7 (API 24) and newer.</li>
index 111cb63416c94f40cd4967122996b55bf491bde1..1595f4a3d04105a63e237b083ca32ac087361132 100644 (file)
@@ -1714,8 +1714,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             Intent viewSourceIntent = new Intent(this, ViewSourceActivity.class);
 
             // Add the variables to the intent.
             Intent viewSourceIntent = new Intent(this, ViewSourceActivity.class);
 
             // Add the variables to the intent.
-            viewSourceIntent.putExtra("user_agent", currentWebView.getSettings().getUserAgentString());
-            viewSourceIntent.putExtra("current_url", currentWebView.getUrl());
+            viewSourceIntent.putExtra(ViewSourceActivityKt.CURRENT_URL, currentWebView.getUrl());
+            viewSourceIntent.putExtra(ViewSourceActivityKt.USER_AGENT, currentWebView.getSettings().getUserAgentString());
 
             // Make it so.
             startActivity(viewSourceIntent);
 
             // Make it so.
             startActivity(viewSourceIntent);
diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/ViewSourceActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/ViewSourceActivity.java
deleted file mode 100644 (file)
index e4899ea..0000000
+++ /dev/null
@@ -1,434 +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.activities;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.res.Configuration;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.LocaleList;
-import android.preference.PreferenceManager;
-import android.text.Spanned;
-import android.text.style.ForegroundColorSpan;
-import android.util.TypedValue;
-import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.EditText;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.appcompat.app.ActionBar;
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.appcompat.widget.Toolbar;
-import androidx.core.app.NavUtils;
-import androidx.fragment.app.DialogFragment;
-import androidx.lifecycle.ViewModelProvider;
-import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
-
-import com.google.android.material.snackbar.Snackbar;
-import com.stoutner.privacybrowser.R;
-import com.stoutner.privacybrowser.dialogs.AboutViewSourceDialog;
-import com.stoutner.privacybrowser.helpers.ProxyHelper;
-import com.stoutner.privacybrowser.viewmodelfactories.WebViewSourceFactory;
-import com.stoutner.privacybrowser.viewmodels.WebViewSource;
-
-import java.net.Proxy;
-import java.util.Locale;
-
-public class ViewSourceActivity extends AppCompatActivity {
-    // `activity` is used in `onCreate()` and `goBack()`.
-    private Activity activity;
-
-    // The color spans are used in `onCreate()` and `highlightUrlText()`.
-    private ForegroundColorSpan redColorSpan;
-    private ForegroundColorSpan initialGrayColorSpan;
-    private ForegroundColorSpan finalGrayColorSpan;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        // Get a handle for the shared preferences.
-        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
-
-        // Get the screenshot preference.
-        boolean allowScreenshots = sharedPreferences.getBoolean("allow_screenshots", false);
-
-        // Disable screenshots if not allowed.
-        if (!allowScreenshots) {
-            getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
-        }
-
-        // Set the theme.
-        setTheme(R.style.PrivacyBrowser);
-
-        // Run the default commands.
-        super.onCreate(savedInstanceState);
-
-        // Get the launching intent
-        Intent intent = getIntent();
-
-        // Get the information from the intent.
-        String userAgent = intent.getStringExtra("user_agent");
-        String currentUrl = intent.getStringExtra("current_url");
-
-        // Remove the incorrect lint warning below that the user agent might be null.
-        assert userAgent != null;
-
-        // Store a handle for the current activity.
-        activity = this;
-
-        // Set the content view.
-        setContentView(R.layout.view_source_coordinatorlayout);
-
-        // Get a handle for the toolbar.
-        Toolbar toolbar = findViewById(R.id.view_source_toolbar);
-
-        // Set the support action bar.
-        setSupportActionBar(toolbar);
-
-        // Get a handle for the action bar.
-        final ActionBar actionBar = getSupportActionBar();
-
-        // Remove the incorrect lint warning that the action bar might be null.
-        assert actionBar != null;
-
-        // Add the custom layout to the action bar.
-        actionBar.setCustomView(R.layout.view_source_app_bar);
-        actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
-
-        // Get handles for the views.
-        EditText urlEditText = findViewById(R.id.url_edittext);
-        TextView requestHeadersTextView = findViewById(R.id.request_headers);
-        TextView responseMessageTextView = findViewById(R.id.response_message);
-        TextView responseHeadersTextView = findViewById(R.id.response_headers);
-        TextView responseBodyTextView = findViewById(R.id.response_body);
-        ProgressBar progressBar = findViewById(R.id.progress_bar);
-        SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.view_source_swiperefreshlayout);
-
-        // Populate the URL text box.
-        urlEditText.setText(currentUrl);
-
-        // Initialize the gray foreground color spans for highlighting the URLs.  The deprecated `getResources()` must be used until API >= 23.
-        initialGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500));
-        finalGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500));
-
-        // Get the current theme status.
-        int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
-
-        // Set the red color span according to the theme.
-        if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
-            redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700));
-        } else {
-            redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_900));
-        }
-
-        // Apply text highlighting to the URL.
-        highlightUrlText();
-
-        // Get a handle for the input method manager, which is used to hide the keyboard.
-        InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
-
-        // Remove the lint warning that the input method manager might be null.
-        assert inputMethodManager != null;
-
-        // Remove the formatting from the URL when the user is editing the text.
-        urlEditText.setOnFocusChangeListener((View v, boolean hasFocus) -> {
-            if (hasFocus) {  // The user is editing `urlTextBox`.
-                // Remove the highlighting.
-                urlEditText.getText().removeSpan(redColorSpan);
-                urlEditText.getText().removeSpan(initialGrayColorSpan);
-                urlEditText.getText().removeSpan(finalGrayColorSpan);
-            } else {  // The user has stopped editing `urlTextBox`.
-                // Hide the soft keyboard.
-                inputMethodManager.hideSoftInputFromWindow(urlEditText.getWindowToken(), 0);
-
-                // Move to the beginning of the string.
-                urlEditText.setSelection(0);
-
-                // Reapply the highlighting.
-                highlightUrlText();
-            }
-        });
-
-        // Set the refresh color scheme according to the theme.
-        if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
-            swipeRefreshLayout.setColorSchemeResources(R.color.blue_700);
-        } else {
-            swipeRefreshLayout.setColorSchemeResources(R.color.violet_500);
-        }
-
-        // Initialize a color background typed value.
-        TypedValue colorBackgroundTypedValue = new TypedValue();
-
-        // Get the color background from the theme.
-        getTheme().resolveAttribute(android.R.attr.colorBackground, colorBackgroundTypedValue, true);
-
-        // Get the color background int from the typed value.
-        int colorBackgroundInt = colorBackgroundTypedValue.data;
-
-        // Set the swipe refresh background color.
-        swipeRefreshLayout.setProgressBackgroundColorSchemeColor(colorBackgroundInt);
-
-        // Get the Do Not Track status.
-        boolean doNotTrack = sharedPreferences.getBoolean("do_not_track", false);
-
-        // Instantiate a locale string.
-        String localeString;
-
-        // Populate the locale string.
-        if (Build.VERSION.SDK_INT >= 24) {  // SDK >= 24 has a list of locales.
-            // Get the list of locales.
-            LocaleList localeList = getResources().getConfiguration().getLocales();
-
-            // Initialize a string builder to extract the locales from the list.
-            StringBuilder localesStringBuilder = new StringBuilder();
-
-            // Initialize a `q` value, which is used by `WebView` to indicate the order of importance of the languages.
-            int q = 10;
-
-            // Populate the string builder with the contents of the locales list.
-            for (int i = 0; i < localeList.size(); i++) {
-                // Append a comma if there is already an item in the string builder.
-                if (i > 0) {
-                    localesStringBuilder.append(",");
-                }
-
-                // Get the locale from the list.
-                Locale locale = localeList.get(i);
-
-                // Add the locale to the string.  `locale` by default displays as `en_US`, but WebView uses the `en-US` format.
-                localesStringBuilder.append(locale.getLanguage());
-                localesStringBuilder.append("-");
-                localesStringBuilder.append(locale.getCountry());
-
-                // If not the first locale, append `;q=0.x`, which drops by .1 for each removal from the main locale until q=0.1.
-                if (q < 10) {
-                    localesStringBuilder.append(";q=0.");
-                    localesStringBuilder.append(q);
-                }
-
-                // Decrement `q` if it is greater than 1.
-                if (q > 1) {
-                    q--;
-                }
-
-                // Add a second entry for the language only portion of the locale.
-                localesStringBuilder.append(",");
-                localesStringBuilder.append(locale.getLanguage());
-
-                // Append `1;q=0.x`, which drops by .1 for each removal form the main locale until q=0.1.
-                localesStringBuilder.append(";q=0.");
-                localesStringBuilder.append(q);
-
-                // Decrement `q` if it is greater than 1.
-                if (q > 1) {
-                    q--;
-                }
-            }
-
-            // Store the populated string builder in the locale string.
-            localeString = localesStringBuilder.toString();
-        } else {  // SDK < 24 only has a primary locale.
-            // Store the locale in the locale string.
-            localeString = Locale.getDefault().toString();
-        }
-
-        // Instantiate the proxy helper.
-        ProxyHelper proxyHelper = new ProxyHelper();
-
-        // Get the current proxy.
-        Proxy proxy = proxyHelper.getCurrentProxy(this);
-
-        // Make the progress bar visible.
-        progressBar.setVisibility(View.VISIBLE);
-
-        // Set the progress bar to be indeterminate.
-        progressBar.setIndeterminate(true);
-
-        // Instantiate the WebView source factory.
-        ViewModelProvider.Factory webViewSourceFactory = new WebViewSourceFactory(currentUrl, userAgent, doNotTrack, localeString, proxy, MainWebViewActivity.executorService);
-
-        // Instantiate the WebView source view model class.
-        final WebViewSource webViewSource = new ViewModelProvider(this, webViewSourceFactory).get(WebViewSource.class);
-
-        // Create a source observer.
-        webViewSource.observeSource().observe(this, sourceStringArray -> {
-            // Populate the text views.  This can take a long time, and freezes the user interface, if the response body is particularly large.
-            requestHeadersTextView.setText(sourceStringArray[0]);
-            responseMessageTextView.setText(sourceStringArray[1]);
-            responseHeadersTextView.setText(sourceStringArray[2]);
-            responseBodyTextView.setText(sourceStringArray[3]);
-
-            // Hide the progress bar.
-            progressBar.setIndeterminate(false);
-            progressBar.setVisibility(View.GONE);
-
-            //Stop the swipe to refresh indicator if it is running
-            swipeRefreshLayout.setRefreshing(false);
-        });
-
-        // Create an error observer.
-        webViewSource.observeErrors().observe(this, errorString -> {
-            // Display an error snackbar if the string is not `""`.
-            if (!errorString.equals("")) {
-                Snackbar.make(swipeRefreshLayout, errorString, Snackbar.LENGTH_LONG).show();
-            }
-        });
-
-        // Implement swipe to refresh.
-        swipeRefreshLayout.setOnRefreshListener(() -> {
-            // Make the progress bar visible.
-            progressBar.setVisibility(View.VISIBLE);
-
-            // Set the progress bar to be indeterminate.
-            progressBar.setIndeterminate(true);
-
-            // Get the URL.
-            String urlString = urlEditText.getText().toString();
-
-            // Get the updated source.
-            webViewSource.updateSource(urlString);
-        });
-
-        // Set the go button on the keyboard to request new source data.
-        urlEditText.setOnKeyListener((View v, int keyCode, KeyEvent event) -> {
-            // Request new source data if the enter key was pressed.
-            if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
-                // Hide the soft keyboard.
-                inputMethodManager.hideSoftInputFromWindow(urlEditText.getWindowToken(), 0);
-
-                // Remove the focus from the URL box.
-                urlEditText.clearFocus();
-
-                // Make the progress bar visible.
-                progressBar.setVisibility(View.VISIBLE);
-
-                // Set the progress bar to be indeterminate.
-                progressBar.setIndeterminate(true);
-
-                // Get the URL.
-                String urlString = urlEditText.getText().toString();
-
-                // Get the updated source.
-                webViewSource.updateSource(urlString);
-
-                // Consume the key press.
-                return true;
-            } else {
-                // Do not consume the key press.
-                return false;
-            }
-        });
-    }
-
-    @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
-        // Inflate the menu.  This adds items to the action bar if it is present.
-        getMenuInflater().inflate(R.menu.view_source_options_menu, menu);
-
-        // Display the menu.
-        return true;
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(@NonNull MenuItem menuItem) {
-        // Get a handle for the about alert dialog.
-        DialogFragment aboutDialogFragment = new AboutViewSourceDialog();
-
-        // Show the about alert dialog.
-        aboutDialogFragment.show(getSupportFragmentManager(), getString(R.string.about));
-
-        // Consume the event.
-        return true;
-    }
-
-    public void goBack(View view) {
-        // Go home.
-        NavUtils.navigateUpFromSameTask(activity);
-    }
-
-    private void highlightUrlText() {
-        // Get a handle for the URL EditText.
-        EditText urlEditText = findViewById(R.id.url_edittext);
-
-        // Get the URL string.
-        String urlString = urlEditText.getText().toString();
-
-        // Highlight the URL according to the protocol.
-        if (urlString.startsWith("file://")) {  // This is a file URL.
-            // De-emphasize only the protocol.
-            urlEditText.getText().setSpan(initialGrayColorSpan, 0, 7, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
-        } else if (urlString.startsWith("content://")) {
-            // De-emphasize only the protocol.
-            urlEditText.getText().setSpan(initialGrayColorSpan, 0, 10, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
-        } else {  // This is a web URL.
-            // Get the index of the `/` immediately after the domain name.
-            int endOfDomainName = urlString.indexOf("/", (urlString.indexOf("//") + 2));
-
-            // Create a base URL string.
-            String baseUrl;
-
-            // Get the base URL.
-            if (endOfDomainName > 0) {  // There is at least one character after the base URL.
-                // Get the base URL.
-                baseUrl = urlString.substring(0, endOfDomainName);
-            } else {  // There are no characters after the base URL.
-                // Set the base URL to be the entire URL string.
-                baseUrl = urlString;
-            }
-
-            // Get the index of the last `.` in the domain.
-            int lastDotIndex = baseUrl.lastIndexOf(".");
-
-            // Get the index of the penultimate `.` in the domain.
-            int penultimateDotIndex = baseUrl.lastIndexOf(".", lastDotIndex - 1);
-
-            // Markup the beginning of the URL.
-            if (urlString.startsWith("http://")) {  // Highlight the protocol of connections that are not encrypted.
-                urlEditText.getText().setSpan(redColorSpan, 0, 7, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
-
-                // De-emphasize subdomains.
-                if (penultimateDotIndex > 0) {  // There is more than one subdomain in the domain name.
-                    urlEditText.getText().setSpan(initialGrayColorSpan, 7, penultimateDotIndex + 1, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
-                }
-            } else if (urlString.startsWith("https://")) {  // De-emphasize the protocol of connections that are encrypted.
-                if (penultimateDotIndex > 0) {  // There is more than one subdomain in the domain name.
-                    // De-emphasize the protocol and the additional subdomains.
-                    urlEditText.getText().setSpan(initialGrayColorSpan, 0, penultimateDotIndex + 1, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
-                } else {  // There is only one subdomain in the domain name.
-                    // De-emphasize only the protocol.
-                    urlEditText.getText().setSpan(initialGrayColorSpan, 0, 8, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
-                }
-            }
-
-            // De-emphasize the text after the domain name.
-            if (endOfDomainName > 0) {
-                urlEditText.getText().setSpan(finalGrayColorSpan, endOfDomainName, urlString.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/ViewSourceActivity.kt b/app/src/main/java/com/stoutner/privacybrowser/activities/ViewSourceActivity.kt
new file mode 100644 (file)
index 0000000..64e4ee9
--- /dev/null
@@ -0,0 +1,420 @@
+/*
+ * 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.activities
+
+import android.content.res.Configuration
+import android.os.Build
+import android.os.Bundle
+import android.text.SpannableStringBuilder
+import android.text.Spanned
+import android.text.style.ForegroundColorSpan
+import android.util.TypedValue
+import android.view.KeyEvent
+import android.view.Menu
+import android.view.MenuItem
+import android.view.View
+import android.view.View.OnFocusChangeListener
+import android.view.WindowManager
+import android.view.inputmethod.InputMethodManager
+import android.widget.EditText
+import android.widget.ProgressBar
+import android.widget.TextView
+
+import androidx.appcompat.app.ActionBar
+import androidx.appcompat.app.AppCompatActivity
+import androidx.appcompat.widget.Toolbar
+import androidx.core.app.NavUtils
+import androidx.fragment.app.DialogFragment
+import androidx.lifecycle.ViewModelProvider
+import androidx.preference.PreferenceManager
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
+
+import com.google.android.material.snackbar.Snackbar
+
+import com.stoutner.privacybrowser.R
+import com.stoutner.privacybrowser.dialogs.AboutViewSourceDialog
+import com.stoutner.privacybrowser.helpers.ProxyHelper
+import com.stoutner.privacybrowser.viewmodelfactories.WebViewSourceFactory
+import com.stoutner.privacybrowser.viewmodels.WebViewSource
+
+import java.util.Locale
+
+// Declare the public constants.
+const val CURRENT_URL = "current_url"
+const val USER_AGENT = "user_agent"
+
+class ViewSourceActivity: AppCompatActivity() {
+    // Declare the class variables.
+    private lateinit var initialGrayColorSpan: ForegroundColorSpan
+    private lateinit var finalGrayColorSpan: ForegroundColorSpan
+    private lateinit var redColorSpan: ForegroundColorSpan
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        // Get a handle for the shared preferences.
+        val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(applicationContext)
+
+        // Get the screenshot preference.
+        val allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false)
+
+        // Disable screenshots if not allowed.
+        if (!allowScreenshots) {
+            window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
+        }
+
+        // Set the theme.
+        setTheme(R.style.PrivacyBrowser)
+
+        // Run the default commands.
+        super.onCreate(savedInstanceState)
+
+        // Get the launching intent
+        val intent = intent
+
+        // Get the information from the intent.
+        val currentUrl = intent.getStringExtra(CURRENT_URL)
+        val userAgent = intent.getStringExtra(USER_AGENT)
+
+        // Set the content view.
+        setContentView(R.layout.view_source_coordinatorlayout)
+
+        // Get a handle for the toolbar.
+        val toolbar = findViewById<Toolbar>(R.id.view_source_toolbar)
+
+        // Set the support action bar.
+        setSupportActionBar(toolbar)
+
+        // Get a handle for the action bar.
+        val actionBar = supportActionBar!!
+
+        // Add the custom layout to the action bar.
+        actionBar.setCustomView(R.layout.view_source_app_bar)
+
+        // Instruct the action bar to display a custom layout.
+        actionBar.displayOptions = ActionBar.DISPLAY_SHOW_CUSTOM
+
+        // Get handles for the views.
+        val urlEditText = findViewById<EditText>(R.id.url_edittext)
+        val requestHeadersTextView = findViewById<TextView>(R.id.request_headers)
+        val responseMessageTextView = findViewById<TextView>(R.id.response_message)
+        val responseHeadersTextView = findViewById<TextView>(R.id.response_headers)
+        val responseBodyTextView = findViewById<TextView>(R.id.response_body)
+        val progressBar = findViewById<ProgressBar>(R.id.progress_bar)
+        val swipeRefreshLayout = findViewById<SwipeRefreshLayout>(R.id.view_source_swiperefreshlayout)
+
+        // Populate the URL text box.
+        urlEditText.setText(currentUrl)
+
+        // Initialize the gray foreground color spans for highlighting the URLs.  The deprecated `getColor()` must be used until the minimum API >= 23.
+        @Suppress("DEPRECATION")
+        initialGrayColorSpan = ForegroundColorSpan(resources.getColor(R.color.gray_500))
+        @Suppress("DEPRECATION")
+        finalGrayColorSpan = ForegroundColorSpan(resources.getColor(R.color.gray_500))
+
+        // Get the current theme status.
+        val currentThemeStatus = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
+
+        // Set the red color span according to the theme.  The deprecated `getColor()` must be used until the minimum API >= 23.
+        redColorSpan = if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+            @Suppress("DEPRECATION")
+            ForegroundColorSpan(resources.getColor(R.color.red_a700))
+        } else {
+            @Suppress("DEPRECATION")
+            ForegroundColorSpan(resources.getColor(R.color.red_900))
+        }
+
+        // Apply text highlighting to the URL.
+        highlightUrlText()
+
+        // Get a handle for the input method manager, which is used to hide the keyboard.
+        val inputMethodManager = (getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager)
+
+        // Remove the formatting from the URL when the user is editing the text.
+        urlEditText.onFocusChangeListener = OnFocusChangeListener { _: View?, hasFocus: Boolean ->
+            if (hasFocus) {  // The user is editing the URL text box.
+                // Remove the highlighting.
+                urlEditText.text.removeSpan(redColorSpan)
+                urlEditText.text.removeSpan(initialGrayColorSpan)
+                urlEditText.text.removeSpan(finalGrayColorSpan)
+            } else {  // The user has stopped editing the URL text box.
+                // Hide the soft keyboard.
+                inputMethodManager.hideSoftInputFromWindow(urlEditText.windowToken, 0)
+
+                // Move to the beginning of the string.
+                urlEditText.setSelection(0)
+
+                // Reapply the highlighting.
+                highlightUrlText()
+            }
+        }
+
+        // Set the refresh color scheme according to the theme.
+        if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+            swipeRefreshLayout.setColorSchemeResources(R.color.blue_700)
+        } else {
+            swipeRefreshLayout.setColorSchemeResources(R.color.violet_500)
+        }
+
+        // Initialize a color background typed value.
+        val colorBackgroundTypedValue = TypedValue()
+
+        // Get the color background from the theme.
+        theme.resolveAttribute(android.R.attr.colorBackground, colorBackgroundTypedValue, true)
+
+        // Get the color background int from the typed value.
+        val colorBackgroundInt = colorBackgroundTypedValue.data
+
+        // Set the swipe refresh background color.
+        swipeRefreshLayout.setProgressBackgroundColorSchemeColor(colorBackgroundInt)
+
+        // Get the Do Not Track status.
+        val doNotTrack = sharedPreferences.getBoolean(getString(R.string.do_not_track_key), false)
+
+        // Populate the locale string.
+        val localeString = if (Build.VERSION.SDK_INT >= 24) {  // SDK >= 24 has a list of locales.
+            // Get the list of locales.
+            val localeList = resources.configuration.locales
+
+            // Initialize a string builder to extract the locales from the list.
+            val localesStringBuilder = StringBuilder()
+
+            // Initialize a `q` value, which is used by `WebView` to indicate the order of importance of the languages.
+            var q = 10
+
+            // Populate the string builder with the contents of the locales list.
+            for (i in 0 until localeList.size()) {
+                // Append a comma if there is already an item in the string builder.
+                if (i > 0) {
+                    localesStringBuilder.append(",")
+                }
+
+                // Get the locale from the list.
+                val locale = localeList[i]
+
+                // Add the locale to the string.  `locale` by default displays as `en_US`, but WebView uses the `en-US` format.
+                localesStringBuilder.append(locale.language)
+                localesStringBuilder.append("-")
+                localesStringBuilder.append(locale.country)
+
+                // If not the first locale, append `;q=0.x`, which drops by .1 for each removal from the main locale until q=0.1.
+                if (q < 10) {
+                    localesStringBuilder.append(";q=0.")
+                    localesStringBuilder.append(q)
+                }
+
+                // Decrement `q` if it is greater than 1.
+                if (q > 1) {
+                    q--
+                }
+
+                // Add a second entry for the language only portion of the locale.
+                localesStringBuilder.append(",")
+                localesStringBuilder.append(locale.language)
+
+                // Append `1;q=0.x`, which drops by .1 for each removal form the main locale until q=0.1.
+                localesStringBuilder.append(";q=0.")
+                localesStringBuilder.append(q)
+
+                // Decrement `q` if it is greater than 1.
+                if (q > 1) {
+                    q--
+                }
+            }
+
+            // Store the populated string builder in the locale string.
+            localesStringBuilder.toString()
+        } else {  // SDK < 24 only has a primary locale.
+            // Store the locale in the locale string.
+            Locale.getDefault().toString()
+        }
+
+        // Instantiate the proxy helper.
+        val proxyHelper = ProxyHelper()
+
+        // Get the current proxy.
+        val proxy = proxyHelper.getCurrentProxy(this)
+
+        // Make the progress bar visible.
+        progressBar.visibility = View.VISIBLE
+
+        // Set the progress bar to be indeterminate.
+        progressBar.isIndeterminate = true
+
+        // Instantiate the WebView source factory.
+        val webViewSourceFactory: ViewModelProvider.Factory = WebViewSourceFactory(currentUrl!!, userAgent!!, doNotTrack, localeString, proxy, MainWebViewActivity.executorService)
+
+        // Instantiate the WebView source view model class.
+        val webViewSource = ViewModelProvider(this, webViewSourceFactory).get(WebViewSource::class.java)
+
+        // Create a source observer.
+        webViewSource.observeSource().observe(this, { sourceStringArray: Array<SpannableStringBuilder> ->
+            // Populate the text views.  This can take a long time, and freezes the user interface, if the response body is particularly large.
+            requestHeadersTextView.text = sourceStringArray[0]
+            responseMessageTextView.text = sourceStringArray[1]
+            responseHeadersTextView.text = sourceStringArray[2]
+            responseBodyTextView.text = sourceStringArray[3]
+
+            // Hide the progress bar.
+            progressBar.isIndeterminate = false
+            progressBar.visibility = View.GONE
+
+            //Stop the swipe to refresh indicator if it is running
+            swipeRefreshLayout.isRefreshing = false
+        })
+
+        // Create an error observer.
+        webViewSource.observeErrors().observe(this, { errorString: String ->
+            // Display an error snackbar if the string is not `""`.
+            if (errorString != "") {
+                Snackbar.make(swipeRefreshLayout, errorString, Snackbar.LENGTH_LONG).show()
+            }
+        })
+
+        // Implement swipe to refresh.
+        swipeRefreshLayout.setOnRefreshListener {
+            // Make the progress bar visible.
+            progressBar.visibility = View.VISIBLE
+
+            // Set the progress bar to be indeterminate.
+            progressBar.isIndeterminate = true
+
+            // Get the URL.
+            val urlString = urlEditText.text.toString()
+
+            // Get the updated source.
+            webViewSource.updateSource(urlString)
+        }
+
+        // Set the go button on the keyboard to request new source data.
+        urlEditText.setOnKeyListener { _: View?, keyCode: Int, event: KeyEvent ->
+            // Request new source data if the enter key was pressed.
+            if (event.action == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER) {
+                // Hide the soft keyboard.
+                inputMethodManager.hideSoftInputFromWindow(urlEditText.windowToken, 0)
+
+                // Remove the focus from the URL box.
+                urlEditText.clearFocus()
+
+                // Make the progress bar visible.
+                progressBar.visibility = View.VISIBLE
+
+                // Set the progress bar to be indeterminate.
+                progressBar.isIndeterminate = true
+
+                // Get the URL.
+                val urlString = urlEditText.text.toString()
+
+                // Get the updated source.
+                webViewSource.updateSource(urlString)
+
+                // Consume the key press.
+                return@setOnKeyListener true
+            } else {
+                // Do not consume the key press.
+                return@setOnKeyListener false
+            }
+        }
+    }
+
+    override fun onCreateOptionsMenu(menu: Menu): Boolean {
+        // Inflate the menu.  This adds items to the action bar if it is present.
+        menuInflater.inflate(R.menu.view_source_options_menu, menu)
+
+        // Display the menu.
+        return true
+    }
+
+    override fun onOptionsItemSelected(menuItem: MenuItem): Boolean {
+        // Get a handle for the about alert dialog.
+        val aboutDialogFragment: DialogFragment = AboutViewSourceDialog()
+
+        // Show the about alert dialog.
+        aboutDialogFragment.show(supportFragmentManager, getString(R.string.about))
+
+        // Consume the event.
+        return true
+    }
+
+    // This method must be named `goBack()` and must have a View argument to match the default back arrow in the app bar.
+    fun goBack(@Suppress("UNUSED_PARAMETER") view: View) {
+        // Go home.
+        NavUtils.navigateUpFromSameTask(this)
+    }
+
+    private fun highlightUrlText() {
+        // Get a handle for the URL edit text.
+        val urlEditText = findViewById<EditText>(R.id.url_edittext)
+
+        // Get the URL string.
+        val urlString = urlEditText.text.toString()
+
+        // Highlight the URL according to the protocol.
+        if (urlString.startsWith("file://")) {  // This is a file URL.
+            // De-emphasize only the protocol.
+            urlEditText.text.setSpan(initialGrayColorSpan, 0, 7, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
+        } else if (urlString.startsWith("content://")) {
+            // De-emphasize only the protocol.
+            urlEditText.text.setSpan(initialGrayColorSpan, 0, 10, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
+        } else {  // This is a web URL.
+            // Get the index of the `/` immediately after the domain name.
+            val endOfDomainName = urlString.indexOf("/", urlString.indexOf("//") + 2)
+
+            // Create a base URL string.
+            val baseUrl: String
+
+            // Get the base URL.
+            baseUrl = if (endOfDomainName > 0) {  // There is at least one character after the base URL.
+                // Get the base URL.
+                urlString.substring(0, endOfDomainName)
+            } else {  // There are no characters after the base URL.
+                // Set the base URL to be the entire URL string.
+                urlString
+            }
+
+            // Get the index of the last `.` in the domain.
+            val lastDotIndex = baseUrl.lastIndexOf(".")
+
+            // Get the index of the penultimate `.` in the domain.
+            val penultimateDotIndex = baseUrl.lastIndexOf(".", lastDotIndex - 1)
+
+            // Markup the beginning of the URL.
+            if (urlString.startsWith("http://")) {  // Highlight the protocol of connections that are not encrypted.
+                urlEditText.text.setSpan(redColorSpan, 0, 7, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
+
+                // De-emphasize subdomains.
+                if (penultimateDotIndex > 0) {  // There is more than one subdomain in the domain name.
+                    urlEditText.text.setSpan(initialGrayColorSpan, 7, penultimateDotIndex + 1, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
+                }
+            } else if (urlString.startsWith("https://")) {  // De-emphasize the protocol of connections that are encrypted.
+                if (penultimateDotIndex > 0) {  // There is more than one subdomain in the domain name.
+                    // De-emphasize the protocol and the additional subdomains.
+                    urlEditText.text.setSpan(initialGrayColorSpan, 0, penultimateDotIndex + 1, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
+                } else {  // There is only one subdomain in the domain name.
+                    // De-emphasize only the protocol.
+                    urlEditText.text.setSpan(initialGrayColorSpan, 0, 8, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
+                }
+            }
+
+            // De-emphasize the text after the domain name.
+            if (endOfDomainName > 0) {
+                urlEditText.text.setSpan(finalGrayColorSpan, endOfDomainName, urlString.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
+            }
+        }
+    }
+}
\ No newline at end of file
index 1311497fbfb72296fc5c84d20032f8cc91af82af..2c900646abe02953edd83431f9f57d14a5d0629a 100644 (file)
@@ -368,4 +368,4 @@ public class GetSourceBackgroundTask {
         // Return the response body string as the result.
         return new SpannableStringBuilder[] {requestHeadersBuilder, responseMessageBuilder, responseHeadersBuilder, responseBodyBuilder};
     }
         // Return the response body string as the result.
         return new SpannableStringBuilder[] {requestHeadersBuilder, responseMessageBuilder, responseHeadersBuilder, responseBodyBuilder};
     }
-}
+}
\ No newline at end of file
index 5f8de2a25657e3fede647b1a321f49658f06fa3b..eeef24f4b8f660640eb7d75a9be0879a12504d1d 100644 (file)
 
 package com.stoutner.privacybrowser.viewmodelfactories
 
 
 package com.stoutner.privacybrowser.viewmodelfactories
 
-import androidx.annotation.Nullable
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.ViewModelProvider
 
 import java.net.Proxy
 import java.util.concurrent.ExecutorService
 
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.ViewModelProvider
 
 import java.net.Proxy
 import java.util.concurrent.ExecutorService
 
-class WebViewSourceFactory (@Nullable private val urlString: String, private val userAgent: String, private val doNotTrack: Boolean, private val localeString: String, private val proxy: Proxy,
+class WebViewSourceFactory (private val urlString: String, private val userAgent: String, private val doNotTrack: Boolean, private val localeString: String, private val proxy: Proxy,
                             private val executorService: ExecutorService): ViewModelProvider.Factory {
     // Override the create function in order to add the provided arguments.
     override fun <T: ViewModel?> create(modelClass: Class<T>): T {
                             private val executorService: ExecutorService): ViewModelProvider.Factory {
     // Override the create function in order to add the provided arguments.
     override fun <T: ViewModel?> create(modelClass: Class<T>): T {
diff --git a/app/src/main/java/com/stoutner/privacybrowser/viewmodels/WebViewSource.java b/app/src/main/java/com/stoutner/privacybrowser/viewmodels/WebViewSource.java
deleted file mode 100644 (file)
index f1ff7f3..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright © 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.viewmodels;
-
-import android.text.SpannableStringBuilder;
-
-import androidx.annotation.Nullable;
-import androidx.lifecycle.LiveData;
-import androidx.lifecycle.MutableLiveData;
-import androidx.lifecycle.ViewModel;
-
-import com.stoutner.privacybrowser.backgroundtasks.GetSourceBackgroundTask;
-
-import java.net.Proxy;
-import java.util.concurrent.ExecutorService;
-
-public class WebViewSource extends ViewModel {
-    // Initialize the mutable live data variables.
-    private final MutableLiveData<SpannableStringBuilder[]> mutableLiveDataSourceStringArray = new MutableLiveData<>();
-    private final MutableLiveData<String> mutableLiveDataErrorString = new MutableLiveData<>();
-
-    // Define the class variables.
-    private final String userAgent;
-    private final boolean doNotTrack;
-    private final String localeString;
-    private final Proxy proxy;
-    private final ExecutorService executorService;
-
-    // The public constructor.
-    public WebViewSource(@Nullable String urlString, String userAgent, boolean doNotTrack, String localeString, Proxy proxy, ExecutorService executorService) {
-        // Store the class variables.
-        this.userAgent = userAgent;
-        this.doNotTrack = doNotTrack;
-        this.localeString = localeString;
-        this.proxy = proxy;
-        this.executorService = executorService;
-
-        // Get the source.
-        updateSource(urlString);
-    }
-
-    // The source observer.
-    public LiveData<SpannableStringBuilder[]> observeSource() {
-        // Return the source to the activity.
-        return mutableLiveDataSourceStringArray;
-    }
-
-    // The error observer.
-    public LiveData<String> observeErrors() {
-        // Return any errors to the activity.
-        return mutableLiveDataErrorString;
-    }
-
-    // The interface for returning the error from the background task
-    public void returnError(String errorString) {
-        // Update the mutable live data error string.
-        mutableLiveDataErrorString.postValue(errorString);
-    }
-
-    // The workhorse that gets the source.
-    public void updateSource(String urlString) {
-        // Reset the mutable live data error string.  This prevents the snackbar it from displaying a later if the activity restarts.
-        mutableLiveDataErrorString.postValue("");
-
-        // Instantiate the get source background task class.
-        GetSourceBackgroundTask getSourceBackgroundTask = new GetSourceBackgroundTask();
-
-        // Get the source.
-        executorService.execute(() -> mutableLiveDataSourceStringArray.postValue(getSourceBackgroundTask.acquire(urlString, userAgent, doNotTrack, localeString, proxy, this)));
-    }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/stoutner/privacybrowser/viewmodels/WebViewSource.kt b/app/src/main/java/com/stoutner/privacybrowser/viewmodels/WebViewSource.kt
new file mode 100644 (file)
index 0000000..1635fe6
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright © 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.viewmodels
+
+import android.text.SpannableStringBuilder
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+
+import com.stoutner.privacybrowser.backgroundtasks.GetSourceBackgroundTask
+
+import java.net.Proxy
+import java.util.concurrent.ExecutorService
+
+class WebViewSource(private val urlString: String, private val userAgent: String, private val doNotTrack: Boolean, private val localeString: String, private val proxy: Proxy,
+                    private val executorService: ExecutorService): ViewModel() {
+    // Initialize the mutable live data variables.
+    private val mutableLiveDataSourceStringArray = MutableLiveData<Array<SpannableStringBuilder>>()
+    private val mutableLiveDataErrorString = MutableLiveData<String>()
+
+    // Initialize the view model.
+    init {
+        // Instantiate the get source background task class.
+        val getSourceBackgroundTask = GetSourceBackgroundTask()
+
+        // Get the source.
+        executorService.execute { mutableLiveDataSourceStringArray.postValue(getSourceBackgroundTask.acquire(urlString, userAgent, doNotTrack, localeString, proxy, this)) }
+    }
+
+    // The source observer.
+    fun observeSource(): LiveData<Array<SpannableStringBuilder>> {
+        // Return the source to the activity.
+        return mutableLiveDataSourceStringArray
+    }
+
+    // The error observer.
+    fun observeErrors(): LiveData<String> {
+        // Return any errors to the activity.
+        return mutableLiveDataErrorString
+    }
+
+    // The interface for returning the error from the background task
+    fun returnError(errorString: String) {
+        // Update the mutable live data error string.
+        mutableLiveDataErrorString.postValue(errorString)
+    }
+
+    // The workhorse that gets the source.
+    fun updateSource(urlString: String) {
+        // Reset the mutable live data error string.  This prevents the snackbar from displaying later if the activity restarts.
+        mutableLiveDataErrorString.postValue("")
+
+        // Instantiate the get source background task class.
+        val getSourceBackgroundTask = GetSourceBackgroundTask()
+
+        // Get the source.
+        executorService.execute { mutableLiveDataSourceStringArray.postValue(getSourceBackgroundTask.acquire(urlString, userAgent, doNotTrack, localeString, proxy, this)) }
+    }
+}
\ No newline at end of file
index 10230d030b66286386835a7285b38954fcf52f65..7a5f352af5bf3fc9926ea84f3a12ef8ed7df66bd 100644 (file)
 
     <!-- Non-translatable preference keys. -->
     <string name="allow_screenshots_key" translatable="false">allow_screenshots</string>
 
     <!-- Non-translatable preference keys. -->
     <string name="allow_screenshots_key" translatable="false">allow_screenshots</string>
+    <string name="do_not_track_key" translatable="false">do_not_track</string>
     <string name="clear_logcat_key" translatable="false">clear_logcat</string>
     <string name="display_additional_app_bar_icons_key" translatable="false">display_additional_app_bar_icons</string>
 
     <string name="clear_logcat_key" translatable="false">clear_logcat</string>
     <string name="display_additional_app_bar_icons_key" translatable="false">display_additional_app_bar_icons</string>
 
index d65b3ccbe296d985c39738d5210e080c60f2e1c0..9fb54db3debafb5f2dfcd211f14ef3772cb6420b 100644 (file)
@@ -26,7 +26,7 @@ buildscript {
     }
     dependencies {
         classpath 'com.android.tools.build:gradle:4.1.1'
     }
     dependencies {
         classpath 'com.android.tools.build:gradle:4.1.1'
-        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.10"
+        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.20"
 
         // NOTE: Do not place your application dependencies here; they belong
         // in the individual module build.gradle files
 
         // NOTE: Do not place your application dependencies here; they belong
         // in the individual module build.gradle files
index 0f9563ad78f7514292d1c031c8db332616eaf502..1f4ffbe26580aefcf87993afae081ff53b76c84b 100644 (file)
@@ -16,8 +16,8 @@
 • Adicione uma entrada Mastodon em Sobre > Links.
 • Faça vários menores melhorias to a user experiência e gráfica interface.
 • Tradução do português brasileiro fornecida por Thiago Nazareno Conceição Silva de Jesus.
 • Adicione uma entrada Mastodon em Sobre > Links.
 • Faça vários menores melhorias to a user experiência e gráfica interface.
 • Tradução do português brasileiro fornecida por Thiago Nazareno Conceição Silva de Jesus.
-• Updated French translation provided by Kévin LE FLOHIC.
-• Updated German translation provided by Bernhard G. Keller.
-• Updated Italian translation provided by Francesco Buratti.
-• Updated Russian translation.
-• Updated Spanish translation provided by Jose A. León.
\ No newline at end of file
+• Tradução francesa atualizada fornecida por Kévin LE FLOHIC.
+• Tradução alemã atualizada fornecida por Bernhard G. Keller.
+• Tradução italiana atualizada fornecida por Francesco Buratti.
+• Tradução russa atualizada.
+• Tradução em espanhol atualizada fornecida por Jose A. León.
\ No newline at end of file
index 0d04ee74441d4485dc270f25cd32d1e24101c2c1..4d02685a56f7db613e2a6baa3027277c81171bc4 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright © 2019 Soren Stoutner <soren@stoutner.com>.
+# Copyright © 2019-2020 Soren Stoutner <soren@stoutner.com>.
 #
 # This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
 #
 #
 # This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
 #
 # You should have received a copy of the GNU General Public License
 # along with Privacy Browser.  If not, see <http://www.gnu.org/licenses/>.
 
 # You should have received a copy of the GNU General Public License
 # along with Privacy Browser.  If not, see <http://www.gnu.org/licenses/>.
 
-
-
-# Project-wide Gradle settings.
-
-# IDE (e.g. Android Studio) users:
-# Gradle settings configured through the IDE *will override*
-# any settings specified in this file.
-
-# For more details on how to configure your build environment visit
+## For more details on how to configure your build environment visit
 # http://www.gradle.org/docs/current/userguide/build_environment.html
 # http://www.gradle.org/docs/current/userguide/build_environment.html
-
+#
 # Specifies the JVM arguments used for the daemon process.
 # The setting is particularly useful for tweaking memory settings.
 # Specifies the JVM arguments used for the daemon process.
 # The setting is particularly useful for tweaking memory settings.
-# org.gradle.jvmargs=-Xmx1536m
-
+# Default value: -Xmx1024m -XX:MaxPermSize=256m
+# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+#
 # When configured, Gradle will run in incubating parallel mode.
 # This option should only be used with decoupled projects. More details, visit
 # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
 # org.gradle.parallel=true
 
 # When configured, Gradle will run in incubating parallel mode.
 # This option should only be used with decoupled projects. More details, visit
 # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
 # org.gradle.parallel=true
 
+# Increase the amount of memory assigned to Gradle and the Kotlin Daemon.
+org.gradle.jvmargs=-Xmx2048M -Dkotlin.daemon.jvm.options\="-Xmx2048M"
+
 # AndroidX package structure to make it clearer which packages are bundled with the
 # Android operating system, and which are packaged with your app's APK
 # https://developer.android.com/topic/libraries/support-library/androidx-rn
 android.useAndroidX=true
 
 # Automatically convert third-party libraries to use AndroidX.  This is necessary for Firebase Ads in the free flavor.
 # AndroidX package structure to make it clearer which packages are bundled with the
 # Android operating system, and which are packaged with your app's APK
 # https://developer.android.com/topic/libraries/support-library/androidx-rn
 android.useAndroidX=true
 
 # Automatically convert third-party libraries to use AndroidX.  This is necessary for Firebase Ads in the free flavor.
-android.enableJetifier=true
\ No newline at end of file
+android.enableJetifier=true