Enable HTTP authentication. Implements https://redmine.stoutner.com/issues/52.
authorSoren Stoutner <soren@stoutner.com>
Mon, 4 Sep 2017 19:32:28 +0000 (12:32 -0700)
committerSoren Stoutner <soren@stoutner.com>
Mon, 4 Sep 2017 19:32:28 +0000 (12:32 -0700)
24 files changed:
.idea/dictionaries/soren.xml
app/src/main/assets/de/about_licenses.html
app/src/main/assets/en/about_licenses.html
app/src/main/assets/en/images/ic_lock.png [new file with mode: 0644]
app/src/main/assets/es/about_licenses.html
app/src/main/assets/es/guide_user_agent.html
app/src/main/assets/it/about_licenses.html
app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java
app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateBookmarkDialog.java
app/src/main/java/com/stoutner/privacybrowser/dialogs/HttpAuthenticationDialog.java [new file with mode: 0644]
app/src/main/java/com/stoutner/privacybrowser/dialogs/PinnedSslCertificateMismatchDialog.java
app/src/main/java/com/stoutner/privacybrowser/dialogs/SslCertificateErrorDialog.java
app/src/main/res/drawable/lock_dark.xml [new file with mode: 0644]
app/src/main/res/drawable/lock_light.xml [new file with mode: 0644]
app/src/main/res/drawable/move_to_folder_dark.xml
app/src/main/res/drawable/move_to_folder_light.xml
app/src/main/res/drawable/ssl_certificate_disabled_dark.xml
app/src/main/res/drawable/ssl_certificate_disabled_light.xml
app/src/main/res/drawable/ssl_certificate_enabled_dark.xml
app/src/main/res/drawable/ssl_certificate_enabled_light.xml
app/src/main/res/layout/http_authentication_dialog.xml [new file with mode: 0644]
app/src/main/res/values-es/strings.xml
app/src/main/res/values-it/strings.xml
app/src/main/res/values/strings.xml

index f6ab929..25a0801 100644 (file)
@@ -92,6 +92,7 @@
       <w>roadmap</w>
       <w>robinlinus</w>
       <w>samsung</w>
+      <w>searx</w>
       <w>securitypatch</w>
       <w>sendto</w>
       <w>showsoftinput</w>
index 7899ba4..23c09ef 100644 (file)
         <p><img class="icon" src="../en/images/ic_list.png"> ic_list.</p>
         <p><img class="icon" src="../en/images/ic_local_activity.png"> ic_local_activity.</p>
         <p><img class="icon" src="../en/images/ic_location_off.png"> ic_location_off.</p>
+        <p><img class="icon" src="../en/images/ic_lock.png"> ic_lock.</p>
         <p><img class="icon" src="../en/images/ic_map.png"> ic_map.</p>
         <p><img class="icon" src="../en/images/ic_more.png"> ic_more.</p>
         <p><img class="icon" src="../en/images/ic_question_answer.png"> ic_question_answer.</p>
index cad98dc..295123d 100644 (file)
@@ -96,6 +96,7 @@
         <p><img class="icon" src="images/ic_list.png"> ic_list.</p>
         <p><img class="icon" src="images/ic_local_activity.png"> ic_local_activity.</p>
         <p><img class="icon" src="images/ic_location_off.png"> ic_location_off.</p>
+        <p><img class="icon" src="images/ic_lock.png"> ic_lock.</p>
         <p><img class="icon" src="images/ic_map.png"> ic_map.</p>
         <p><img class="icon" src="images/ic_more.png"> ic_more.</p>
         <p><img class="icon" src="images/ic_question_answer.png"> ic_question_answer.</p>
diff --git a/app/src/main/assets/en/images/ic_lock.png b/app/src/main/assets/en/images/ic_lock.png
new file mode 100644 (file)
index 0000000..dba1a27
Binary files /dev/null and b/app/src/main/assets/en/images/ic_lock.png differ
index 55df760..633afd4 100644 (file)
         <p><img class="icon" src="../en/images/ic_list.png"> ic_list.</p>
         <p><img class="icon" src="../en/images/ic_local_activity.png"> ic_local_activity.</p>
         <p><img class="icon" src="../en/images/ic_location_off.png"> ic_location_off.</p>
+        <p><img class="icon" src="../en/images/ic_lock.png"> ic_lock.</p>
         <p><img class="icon" src="../en/images/ic_map.png"> ic_map.</p>
         <p><img class="icon" src="../en/images/ic_more.png"> ic_more.</p>
         <p><img class="icon" src="../en/images/ic_question_answer.png"> ic_question_answer.</p>
index f32e67d..813eb5f 100644 (file)
@@ -75,6 +75,8 @@
             Firefox o Chrome son los usuarios de agente más comunes, pero se actualizan automáticamente y sus números de versión cambian tan rápidamente que es probable que los usuarios de agente incluídos
             en Privacy Browser no estén ya en sintonía con la mayoría de agentes de usuario en los registros del servidor.</p>
 
-        <p>El WebView de android no permite que el agente de usuario esté en blanco. Si lo está, WebView simplemente envía el agente de usuario por defecto al servidor.</p>
+        <p>Algunas páginas web<a href="https://www.stoutner.com/user-agent-problems/">no funcionan correctamente</a> si no reconocen el agente de usuario.
+            Usando la configuración de dominios para establecer el agente de usuario a <strong>WebView por defecto</strong>, o a otro agente de usuario que sea normalmente reconocido, suele resolver el problema.
+            El WebView de android no permite que el agente de usuario esté en blanco. Si lo está, WebView simplemente envía el agente de usuario por defecto al servidor.</p>
     </body>
 </html>
\ No newline at end of file
index 23ee075..7e38996 100644 (file)
         <p><img class="icon" src="../en/images/ic_list.png"> ic_list.</p>
         <p><img class="icon" src="../en/images/ic_local_activity.png"> ic_local_activity.</p>
         <p><img class="icon" src="../en/images/ic_location_off.png"> ic_location_off.</p>
+        <p><img class="icon" src="../en/images/ic_lock.png"> ic_lock.</p>
         <p><img class="icon" src="../en/images/ic_map.png"> ic_map.</p>
         <p><img class="icon" src="../en/images/ic_more.png"> ic_more.</p>
         <p><img class="icon" src="../en/images/ic_question_answer.png"> ic_question_answer.</p>
index e16901d..b13a785 100644 (file)
@@ -74,6 +74,7 @@ import android.view.WindowManager;
 import android.view.inputmethod.InputMethodManager;
 import android.webkit.CookieManager;
 import android.webkit.DownloadListener;
+import android.webkit.HttpAuthHandler;
 import android.webkit.SslErrorHandler;
 import android.webkit.WebBackForwardList;
 import android.webkit.WebChromeClient;
@@ -95,6 +96,7 @@ import com.stoutner.privacybrowser.BuildConfig;
 import com.stoutner.privacybrowser.R;
 import com.stoutner.privacybrowser.dialogs.CreateHomeScreenShortcutDialog;
 import com.stoutner.privacybrowser.dialogs.DownloadImageDialog;
+import com.stoutner.privacybrowser.dialogs.HttpAuthenticationDialog;
 import com.stoutner.privacybrowser.dialogs.PinnedSslCertificateMismatchDialog;
 import com.stoutner.privacybrowser.dialogs.UrlHistoryDialog;
 import com.stoutner.privacybrowser.dialogs.ViewSslCertificateDialog;
@@ -121,11 +123,12 @@ import java.util.Set;
 
 // We need to use AppCompatActivity from android.support.v7.app.AppCompatActivity to have access to the SupportActionBar until the minimum API is >= 21.
 public class MainWebViewActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener, CreateHomeScreenShortcutDialog.CreateHomeScreenSchortcutListener,
-        PinnedSslCertificateMismatchDialog.PinnedSslCertificateMismatchListener, SslCertificateErrorDialog.SslCertificateErrorListener, DownloadFileDialog.DownloadFileListener, DownloadImageDialog.DownloadImageListener, UrlHistoryDialog.UrlHistoryListener {
+        HttpAuthenticationDialog.HttpAuthenticationListener, PinnedSslCertificateMismatchDialog.PinnedSslCertificateMismatchListener, SslCertificateErrorDialog.SslCertificateErrorListener, DownloadFileDialog.DownloadFileListener,
+        DownloadImageDialog.DownloadImageListener, UrlHistoryDialog.UrlHistoryListener {
 
     // `darkTheme` is public static so it can be accessed from `AboutActivity`, `GuideActivity`, `AddDomainDialog`, `SettingsActivity`, `DomainsActivity`, `DomainsListFragment`, `BookmarksActivity`, `BookmarksDatabaseViewActivity`,
-    // `CreateBookmarkDialog`, `CreateBookmarkFolderDialog`, `DownloadFileDialog`, `DownloadImageDialog`, `EditBookmarkDialog`, `EditBookmarkFolderDialog`, `MoveToFolderDialog`, `SslCertificateErrorDialog`, `UrlHistoryDialog`, `ViewSslCertificateDialog`,
-    // `CreateHomeScreenShortcutDialog`, and `OrbotProxyHelper`. It is also used in `onCreate()`, `applyAppSettings()`, `applyDomainSettings()`, and `updatePrivacyIcons()`.
+    // `CreateBookmarkDialog`, `CreateBookmarkFolderDialog`, `DownloadFileDialog`, `DownloadImageDialog`, `EditBookmarkDialog`, `EditBookmarkFolderDialog`, `HttpAuthenticationDialog`, `MoveToFolderDialog`, `SslCertificateErrorDialog`, `UrlHistoryDialog`,
+    // `ViewSslCertificateDialog`, `CreateHomeScreenShortcutDialog`, and `OrbotProxyHelper`. It is also used in `onCreate()`, `applyAppSettings()`, `applyDomainSettings()`, and `updatePrivacyIcons()`.
     public static boolean darkTheme;
 
     // `favoriteIconBitmap` is public static so it can be accessed from `CreateHomeScreenShortcutDialog`, `BookmarksActivity`, `CreateBookmarkDialog`, `CreateBookmarkFolderDialog`, `EditBookmarkDialog`, `EditBookmarkFolderDialog`,
@@ -303,6 +306,9 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
     // `sslErrorHandler` is used in `onCreate()`, `onSslErrorCancel()`, and `onSslErrorProceed`.
     private SslErrorHandler sslErrorHandler;
 
+    // `httpAuthHandler` is used in `onCreate()`, `onHttpAuthenticationCancel()`, and `onHttpAuthenticationProceed()`.
+    private static HttpAuthHandler httpAuthHandler;
+
     // `inputMethodManager` is used in `onOptionsItemSelected()`, `loadUrlFromTextBox()`, and `closeFindOnPage()`.
     private InputMethodManager inputMethodManager;
 
@@ -729,6 +735,17 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
                 }
             }
 
+            // Handle HTTP authentication requests.
+            @Override
+            public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) {
+                // Store `handler` so it can be accessed from `onHttpAuthenticationCancel()` and `onHttpAuthenticationProceed()`.
+                httpAuthHandler = handler;
+
+                // Display the HTTP authentication dialog.
+                AppCompatDialogFragment httpAuthenticationDialogFragment = HttpAuthenticationDialog.displayDialog(host, realm);
+                httpAuthenticationDialogFragment.show(getSupportFragmentManager(), getString(R.string.http_authentication));
+            }
+
             // Update the URL in urlTextBox when the page starts to load.
             @Override
             public void onPageStarted(WebView view, String url, Bitmap favicon) {
@@ -867,7 +884,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
                                 !currentWebsiteSslStartDateString.equals(pinnedDomainSslStartDateString) || !currentWebsiteSslEndDateString.equals(pinnedDomainSslEndDateString)) {  // The pinned SSL certificate doesn't match the current domain certificate.
                             //Display the pinned SSL certificate mismatch `AlertDialog`.
                             AppCompatDialogFragment pinnedSslCertificateMismatchDialogFragment = new PinnedSslCertificateMismatchDialog();
-                            pinnedSslCertificateMismatchDialogFragment.show(getSupportFragmentManager(), getResources().getString(R.string.ssl_certificate_mismatch));
+                            pinnedSslCertificateMismatchDialogFragment.show(getSupportFragmentManager(), getString(R.string.ssl_certificate_mismatch));
                         }
                     }
                 }
@@ -902,7 +919,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
 
                     // Display the SSL error `AlertDialog`.
                     AppCompatDialogFragment sslCertificateErrorDialogFragment = SslCertificateErrorDialog.displayDialog(error);
-                    sslCertificateErrorDialogFragment.show(getSupportFragmentManager(), getResources().getString(R.string.ssl_certificate_error));
+                    sslCertificateErrorDialogFragment.show(getSupportFragmentManager(), getString(R.string.ssl_certificate_error));
                 }
             }
         });
@@ -1005,7 +1022,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
             public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {
                 // Show the `DownloadFileDialog` `AlertDialog` and name this instance `@string/download`.
                 AppCompatDialogFragment downloadFileDialogFragment = DownloadFileDialog.fromUrl(url, contentDisposition, contentLength);
-                downloadFileDialogFragment.show(getSupportFragmentManager(), getResources().getString(R.string.download));
+                downloadFileDialogFragment.show(getSupportFragmentManager(), getString(R.string.download));
             }
         });
 
@@ -1260,47 +1277,47 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
         // Prepare the font size title and current size menu item.
         switch (fontSize) {
             case 25:
-                fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.twenty_five_percent);
+                fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.twenty_five_percent);
                 selectedFontSizeMenuItem = menu.findItem(R.id.font_size_twenty_five_percent);
                 break;
 
             case 50:
-                fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.fifty_percent);
+                fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.fifty_percent);
                 selectedFontSizeMenuItem = menu.findItem(R.id.font_size_fifty_percent);
                 break;
 
             case 75:
-                fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.seventy_five_percent);
+                fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.seventy_five_percent);
                 selectedFontSizeMenuItem = menu.findItem(R.id.font_size_seventy_five_percent);
                 break;
 
             case 100:
-                fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.one_hundred_percent);
+                fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.one_hundred_percent);
                 selectedFontSizeMenuItem = menu.findItem(R.id.font_size_one_hundred_percent);
                 break;
 
             case 125:
-                fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.one_hundred_twenty_five_percent);
+                fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.one_hundred_twenty_five_percent);
                 selectedFontSizeMenuItem = menu.findItem(R.id.font_size_one_hundred_twenty_five_percent);
                 break;
 
             case 150:
-                fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.one_hundred_fifty_percent);
+                fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.one_hundred_fifty_percent);
                 selectedFontSizeMenuItem = menu.findItem(R.id.font_size_one_hundred_fifty_percent);
                 break;
 
             case 175:
-                fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.one_hundred_seventy_five_percent);
+                fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.one_hundred_seventy_five_percent);
                 selectedFontSizeMenuItem = menu.findItem(R.id.font_size_one_hundred_seventy_five_percent);
                 break;
 
             case 200:
-                fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.two_hundred_percent);
+                fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.two_hundred_percent);
                 selectedFontSizeMenuItem = menu.findItem(R.id.font_size_two_hundred_percent);
                 break;
 
             default:
-                fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.one_hundred_percent);
+                fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.one_hundred_percent);
                 selectedFontSizeMenuItem = menu.findItem(R.id.font_size_one_hundred_percent);
                 break;
         }
@@ -1631,13 +1648,13 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
                 PrintDocumentAdapter printDocumentAdapter = mainWebView.createPrintDocumentAdapter();
 
                 // Print the document.  The print attributes are `null`.
-                printManager.print(getResources().getString(R.string.privacy_browser_web_page), printDocumentAdapter, null);
+                printManager.print(getString(R.string.privacy_browser_web_page), printDocumentAdapter, null);
                 return true;
 
             case R.id.add_to_homescreen:
                 // Show the `CreateHomeScreenShortcutDialog` `AlertDialog` and name this instance `R.string.create_shortcut`.
                 AppCompatDialogFragment createHomeScreenShortcutDialogFragment = new CreateHomeScreenShortcutDialog();
-                createHomeScreenShortcutDialogFragment.show(getSupportFragmentManager(), getResources().getString(R.string.create_shortcut));
+                createHomeScreenShortcutDialogFragment.show(getSupportFragmentManager(), getString(R.string.create_shortcut));
 
                 //Everything else will be handled by `CreateHomeScreenShortcutDialog` and the associated listener below.
                 return true;
@@ -1689,7 +1706,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
 
                 // Show the `UrlHistoryDialog` `AlertDialog` and name this instance `R.string.history`.  `this` is the `Context`.
                 AppCompatDialogFragment urlHistoryDialogFragment = UrlHistoryDialog.loadBackForwardList(this, webBackForwardList);
-                urlHistoryDialogFragment.show(getSupportFragmentManager(), getResources().getString(R.string.history));
+                urlHistoryDialogFragment.show(getSupportFragmentManager(), getString(R.string.history));
                 break;
 
             case R.id.bookmarks:
@@ -1921,7 +1938,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
                     @Override
                     public boolean onMenuItemClick(MenuItem item) {
                         // Save the link URL in a `ClipData`.
-                        ClipData srcAnchorTypeClipData = ClipData.newPlainText(getResources().getString(R.string.url), linkUrl);
+                        ClipData srcAnchorTypeClipData = ClipData.newPlainText(getString(R.string.url), linkUrl);
 
                         // Set the `ClipData` as the clipboard's primary clip.
                         clipboardManager.setPrimaryClip(srcAnchorTypeClipData);
@@ -1964,7 +1981,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
                     @Override
                     public boolean onMenuItemClick(MenuItem item) {
                         // Save the email address in a `ClipData`.
-                        ClipData srcEmailTypeClipData = ClipData.newPlainText(getResources().getString(R.string.email_address), linkUrl);
+                        ClipData srcEmailTypeClipData = ClipData.newPlainText(getString(R.string.email_address), linkUrl);
 
                         // Set the `ClipData` as the clipboard's primary clip.
                         clipboardManager.setPrimaryClip(srcEmailTypeClipData);
@@ -1999,7 +2016,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
                     public boolean onMenuItemClick(MenuItem item) {
                         // Show the `DownloadImageDialog` `AlertDialog` and name this instance `@string/download`.
                         AppCompatDialogFragment downloadImageDialogFragment = DownloadImageDialog.imageUrl(imageUrl);
-                        downloadImageDialogFragment.show(getSupportFragmentManager(), getResources().getString(R.string.download));
+                        downloadImageDialogFragment.show(getSupportFragmentManager(), getString(R.string.download));
                         return false;
                     }
                 });
@@ -2009,7 +2026,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
                     @Override
                     public boolean onMenuItemClick(MenuItem item) {
                         // Save the image URL in a `ClipData`.
-                        ClipData srcImageTypeClipData = ClipData.newPlainText(getResources().getString(R.string.url), imageUrl);
+                        ClipData srcImageTypeClipData = ClipData.newPlainText(getString(R.string.url), imageUrl);
 
                         // Set the `ClipData` as the clipboard's primary clip.
                         clipboardManager.setPrimaryClip(srcImageTypeClipData);
@@ -2045,7 +2062,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
                     public boolean onMenuItemClick(MenuItem item) {
                         // Show the `DownloadImageDialog` `AlertDialog` and name this instance `@string/download`.
                         AppCompatDialogFragment downloadImageDialogFragment = DownloadImageDialog.imageUrl(imageUrl);
-                        downloadImageDialogFragment.show(getSupportFragmentManager(), getResources().getString(R.string.download));
+                        downloadImageDialogFragment.show(getSupportFragmentManager(), getString(R.string.download));
                         return false;
                     }
                 });
@@ -2055,7 +2072,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
                     @Override
                     public boolean onMenuItemClick(MenuItem item) {
                         // Save the image URL in a `ClipData`.
-                        ClipData srcImageAnchorTypeClipData = ClipData.newPlainText(getResources().getString(R.string.url), imageUrl);
+                        ClipData srcImageAnchorTypeClipData = ClipData.newPlainText(getString(R.string.url), imageUrl);
 
                         // Set the `ClipData` as the clipboard's primary clip.
                         clipboardManager.setPrimaryClip(srcImageAnchorTypeClipData);
@@ -2183,10 +2200,26 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
         }
     }
 
+    @Override
+    public void onHttpAuthenticationCancel() {
+        // Cancel the `HttpAuthHandler`.
+        httpAuthHandler.cancel();
+    }
+
+    @Override
+    public void onHttpAuthenticationProceed(AppCompatDialogFragment dialogFragment) {
+        // Get handles for the `EditTexts`.
+        EditText usernameEditText = (EditText) dialogFragment.getDialog().findViewById(R.id.http_authentication_username);
+        EditText passwordEditText = (EditText) dialogFragment.getDialog().findViewById(R.id.http_authentication_password);
+
+        // Proceed with the HTTP authentication.
+        httpAuthHandler.proceed(usernameEditText.getText().toString(), passwordEditText.getText().toString());
+    }
+
     public void viewSslCertificate(View view) {
         // Show the `ViewSslCertificateDialog` `AlertDialog` and name this instance `@string/view_ssl_certificate`.
         DialogFragment viewSslCertificateDialogFragment = new ViewSslCertificateDialog();
-        viewSslCertificateDialogFragment.show(getFragmentManager(), getResources().getString(R.string.view_ssl_certificate));
+        viewSslCertificateDialogFragment.show(getFragmentManager(), getString(R.string.view_ssl_certificate));
     }
 
     @Override
index 60863a9..b2966c1 100644 (file)
@@ -96,7 +96,7 @@ public class CreateBookmarkDialog extends AppCompatDialogFragment {
         dialogBuilder.setPositiveButton(R.string.create, new DialogInterface.OnClickListener() {
             @Override
             public void onClick(DialogInterface dialog, int which) {
-                // Return the `DialogFragment` to the parent activity on create.
+                // Return the `DialogFragment` to the parent activity.
                 createBookmarkListener.onCreateBookmark(CreateBookmarkDialog.this);
             }
         });
@@ -111,7 +111,7 @@ public class CreateBookmarkDialog extends AppCompatDialogFragment {
         // Show the keyboard when the `AlertDialog` is displayed on the screen.
         alertDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
 
-        // We need to show the `AlertDialog` before we can call `setOnKeyListener()` below.
+        // The `AlertDialog` needs to be shown before `setOnKeyListener()` can be called.
         alertDialog.show();
 
         // Get a handle for `create_bookmark_name_edittext`.
@@ -127,8 +127,10 @@ public class CreateBookmarkDialog extends AppCompatDialogFragment {
                 if ((keyCode == KeyEvent.KEYCODE_ENTER) && (event.getAction() == KeyEvent.ACTION_DOWN)) {
                     // Trigger `createBookmarkListener` and return the `DialogFragment` to the parent activity.
                     createBookmarkListener.onCreateBookmark(CreateBookmarkDialog.this);
+
                     // Manually dismiss the `AlertDialog`.
                     alertDialog.dismiss();
+
                     // Consume the event.
                     return true;
                 } else {  // If any other key was pressed, do not consume the event.
@@ -148,8 +150,10 @@ public class CreateBookmarkDialog extends AppCompatDialogFragment {
                 if ((keyCode == KeyEvent.KEYCODE_ENTER) && (event.getAction() == KeyEvent.ACTION_DOWN)) {
                     // Trigger `createBookmarkListener` and return the DialogFragment to the parent activity.
                     createBookmarkListener.onCreateBookmark(CreateBookmarkDialog.this);
+
                     // Manually dismiss the `AlertDialog`.
                     alertDialog.dismiss();
+
                     // Consume the event.
                     return true;
                 } else { // If any other key was pressed, do not consume the event.
diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/HttpAuthenticationDialog.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/HttpAuthenticationDialog.java
new file mode 100644 (file)
index 0000000..993c8c3
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * Copyright © 2017 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.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+// `AppCompatDialogFragment` is used instead of `DialogFragment` to avoid an error on API <=22.
+import android.support.v7.app.AppCompatDialogFragment;
+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.WindowManager;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import com.stoutner.privacybrowser.R;
+import com.stoutner.privacybrowser.activities.MainWebViewActivity;
+
+public class HttpAuthenticationDialog extends AppCompatDialogFragment{
+
+    // The private variables are used in `onCreate()` and `onCreateDialog()`.
+    private String httpAuthHost;
+    private String httpAuthRealm;
+
+    public static HttpAuthenticationDialog displayDialog(String host, String realm) {
+        // Store the strings in a `Bundle`.
+        Bundle argumentsBundle = new Bundle();
+        argumentsBundle.putString("Host", host);
+        argumentsBundle.putString("Realm", realm);
+
+        // Add `argumentsBundle` to this instance of `HttpAuthenticationDialog`.
+        HttpAuthenticationDialog thisHttpAuthenticationDialog = new HttpAuthenticationDialog();
+        thisHttpAuthenticationDialog.setArguments(argumentsBundle);
+        return thisHttpAuthenticationDialog;
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // Save the host and realm in class variables.
+        httpAuthHost = getArguments().getString("Host");
+        httpAuthRealm = getArguments().getString("Realm");
+    }
+
+    // The public interface is used to send information back to the parent activity.
+    public interface HttpAuthenticationListener {
+        void onHttpAuthenticationCancel();
+
+        void onHttpAuthenticationProceed(AppCompatDialogFragment dialogFragment);
+    }
+
+    // `httpAuthenticationListener` is used in `onAttach()` and `onCreateDialog()`
+    private HttpAuthenticationListener httpAuthenticationListener;
+
+    public void onAttach(Context context) {
+        super.onAttach(context);
+
+        // Get a handle for `httpAuthenticationListener` from `context`.
+        try {
+            httpAuthenticationListener = (HttpAuthenticationListener) context;
+        } catch(ClassCastException exception) {
+            throw new ClassCastException(context.toString() + " must implement `HttpAuthenticationListener`.");
+        }
+    }
+
+    // `@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 activity's layout inflater.
+        LayoutInflater layoutInflater = getActivity().getLayoutInflater();
+
+        // Use `AlertDialog.Builder` to create the `AlertDialog`.
+        AlertDialog.Builder dialogBuilder;
+
+        // Set the style according to the theme.
+        if (MainWebViewActivity.darkTheme) {
+            // Set the dialog theme.
+            dialogBuilder = new AlertDialog.Builder(getActivity(), R.style.PrivacyBrowserAlertDialogDark);
+
+            // Set the icon.
+            dialogBuilder.setIcon(R.drawable.lock_dark);
+        } else {
+            // Set the dialog theme.
+            dialogBuilder = new AlertDialog.Builder(getActivity(), R.style.PrivacyBrowserAlertDialogLight);
+
+            // Set the icon.
+            dialogBuilder.setIcon(R.drawable.lock_light);
+        }
+
+        // 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 negative button.
+        dialogBuilder.setNegativeButton(R.string.close, new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                // Call `onHttpAuthenticationCancel()` and return the `DialogFragment` to the parent activity.
+                httpAuthenticationListener.onHttpAuthenticationCancel();
+            }
+        });
+
+        // Setup the positive button.
+        dialogBuilder.setPositiveButton(R.string.proceed, new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                // Call `onHttpAuthenticationProceed()` and return the `DialogFragment` to the parent activity.
+                httpAuthenticationListener.onHttpAuthenticationProceed(HttpAuthenticationDialog.this);
+            }
+        });
+
+        // Create an `AlertDialog` from the `AlertDialog.Builder`.
+        final AlertDialog alertDialog = dialogBuilder.create();
+
+        // Remove the warning below that `setSoftInputMode` might produce `java.lang.NullPointerException`.
+        assert alertDialog.getWindow() != null;
+
+        // Show the keyboard when the `AlertDialog` is displayed on the screen.
+        alertDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
+
+        // The `AlertDialog` needs to be shown before `setOnKeyListener()` can be called.
+        alertDialog.show();
+
+        // Get handles for the views in `alertDialog`.
+        TextView realmTextView = (TextView) alertDialog.findViewById(R.id.http_authentication_realm);
+        TextView hostTextView = (TextView) alertDialog.findViewById(R.id.http_authentication_host);
+        EditText usernameEditText = (EditText) alertDialog.findViewById(R.id.http_authentication_username);
+        EditText passwordEditText = (EditText) alertDialog.findViewById(R.id.http_authentication_password);
+
+        // Set the realm text.
+        realmTextView.setText(httpAuthRealm);
+
+        // Set the realm text color according to the theme.  The deprecated `.getColor()` must be used until API >= 23.
+        if (MainWebViewActivity.darkTheme) {
+            //noinspection deprecation
+            realmTextView.setTextColor(getResources().getColor(R.color.gray_300));
+        } else {
+            //noinspection deprecation
+            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 `blueColorSpan` according to the theme.  The deprecated `getColor()` must be used until API >= 23.
+        if (MainWebViewActivity.darkTheme) {
+            //noinspection deprecation
+            blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.blue_400));
+        } else {
+            //noinspection deprecation
+            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(new View.OnKeyListener() {
+            public boolean onKey(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)) {
+                    // Trigger `onHttpAuthenticationProceed` and return the `DialogFragment` to the parent activity.
+                    httpAuthenticationListener.onHttpAuthenticationProceed(HttpAuthenticationDialog.this);
+
+                    // Manually dismiss the `AlertDialog`.
+                    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(new View.OnKeyListener() {
+            public boolean onKey(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)) {
+                    // Trigger `onHttpAuthenticationProceed` and return the `DialogFragment` to the parent activity.
+                    httpAuthenticationListener.onHttpAuthenticationProceed(HttpAuthenticationDialog.this);
+
+                    // Manually dismiss the `AlertDialog`.
+                    alertDialog.dismiss();
+
+                    // Consume the event.
+                    return true;
+                } else {  // If any other key was pressed, do not consume the event.
+                    return false;
+                }
+            }
+        });
+
+        // `onCreateDialog()` requires the return of an `AlertDialog`.
+        return alertDialog;
+    }
+}
index aa5b627..d3315ad 100644 (file)
@@ -29,7 +29,7 @@ import android.os.Bundle;
 import android.support.annotation.NonNull;
 import android.support.design.widget.TabLayout;
 import android.support.v4.view.PagerAdapter;
-// We have to use `AppCompatDialogFragment` instead of `DialogFragment` or an error is produced on API <=22.
+// `AppCompatDialogFragment` is used instead of `DialogFragment` to avoid an error on API <=22.
 import android.support.v7.app.AppCompatDialogFragment;
 import android.text.SpannableStringBuilder;
 import android.text.Spanned;
@@ -47,8 +47,6 @@ import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper;
 import java.text.DateFormat;
 import java.util.Date;
 
-// `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`.
-@SuppressLint("InflateParams")
 public class PinnedSslCertificateMismatchDialog extends AppCompatDialogFragment {
     // `layoutInflater` is used in `onCreateDialog()` and `pagerAdapter`.
     private LayoutInflater layoutInflater;
@@ -84,6 +82,9 @@ public class PinnedSslCertificateMismatchDialog extends AppCompatDialogFragment
         }
     }
 
+    // `@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 activity's layout inflater.
index e966b2e..00a3b1f 100644 (file)
@@ -28,7 +28,7 @@ import android.net.http.SslCertificate;
 import android.net.http.SslError;
 import android.os.Bundle;
 import android.support.annotation.NonNull;
-// We have to use `AppCompatDialogFragment` instead of `DialogFragment` or an error is produced on API <= 22.
+// `AppCompatDialogFragment` is used instead of `DialogFragment` to avoid an error on API <=22.
 import android.support.v7.app.AppCompatDialogFragment;
 import android.text.SpannableStringBuilder;
 import android.text.Spanned;
@@ -44,6 +44,7 @@ import java.util.Date;
 
 public class SslCertificateErrorDialog extends AppCompatDialogFragment {
 
+    // The private variables are used in `onCreate()` and `onCreateDialog()`.
     private int primaryErrorInt;
     private String urlWithError;
     private String issuedToCName;
diff --git a/app/src/main/res/drawable/lock_dark.xml b/app/src/main/res/drawable/lock_dark.xml
new file mode 100644 (file)
index 0000000..9453cee
--- /dev/null
@@ -0,0 +1,13 @@
+<!-- `lock_dark.xml` comes from the Android Material icon set, where it is called `ic_lock`.  It is released under the Apache License 2.0. -->
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:width="24dp"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0" >
+
+    <!-- A hard coded color must be used until API >= 21.  Then `@color` may be used. -->
+    <path
+        android:fillColor="#FF1E88E5"
+        android:pathData="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM12,17c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM15.1,8L8.9,8L8.9,6c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2z"/>
+</vector>
diff --git a/app/src/main/res/drawable/lock_light.xml b/app/src/main/res/drawable/lock_light.xml
new file mode 100644 (file)
index 0000000..3c8b86a
--- /dev/null
@@ -0,0 +1,13 @@
+<!-- `lock_light.xml` comes from the Android Material icon set, where it is called `ic_lock`.  It is released under the Apache License 2.0. -->
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:width="24dp"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0" >
+
+    <!-- A hard coded color must be used until API >= 21.  Then `@color` may be used. -->
+    <path
+        android:fillColor="#FF1565C0"
+        android:pathData="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM12,17c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM15.1,8L8.9,8L8.9,6c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2z"/>
+</vector>
index 16dc955..b893e40 100644 (file)
@@ -11,7 +11,7 @@
     android:autoMirrored="true"
     tools:ignore="VectorRaster" >
 
-    <!-- We have to use a hard coded color until API >= 21.  Then we can use `@color`. -->
+    <!-- A hard coded color must be used until API >= 21.  Then `@color` may be used. -->
     <path
         android:fillColor="#FFE0E0E0"
         android:pathData="M20,6h-8l-2,-2L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,8c0,-1.1 -0.9,-2 -2,-2zM17.94,17L15,15.28 12.06,17l0.78,-3.33 -2.59,-2.24 3.41,-0.29L15,8l1.34,3.14 3.41,0.29 -2.59,2.24 0.78,3.33z"/>
index 92e9b19..98ec8c2 100644 (file)
@@ -11,7 +11,7 @@
     android:autoMirrored="true"
     tools:ignore="VectorRaster" >
 
-    <!-- We have to use a hard coded color until API >= 21.  Then we can use `@color`. -->
+    <!-- A hard coded color must be used until API >= 21.  Then `@color` may be used. -->
     <path
         android:fillColor="#FFFFFFFF"
         android:pathData="M20,6h-8l-2,-2L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,8c0,-1.1 -0.9,-2 -2,-2zM17.94,17L15,15.28 12.06,17l0.78,-3.33 -2.59,-2.24 3.41,-0.29L15,8l1.34,3.14 3.41,0.29 -2.59,2.24 0.78,3.33z"/>
index 3f6998c..425c372 100644 (file)
@@ -11,7 +11,7 @@
     android:autoMirrored="true"
     tools:ignore="VectorRaster" >
 
-    <!-- We have to use a hard coded color until API >= 21.  Then we can use `@color`. -->
+    <!-- A hard coded color must be used until API >= 21.  Then `@color` may be used. -->
     <path
         android:fillColor="#FF9E9E9E"
         android:pathData="M22,4v-0.5C22,2.12 20.88,1 19.5,1S17,2.12 17,3.5L17,4c-0.55,0 -1,0.45 -1,1v4c0,0.55 0.45,1 1,1h5c0.55,0 1,-0.45 1,-1L23,5c0,-0.55 -0.45,-1 -1,-1zM21.2,4h-3.4v-0.5c0,-0.94 0.76,-1.7 1.7,-1.7s1.7,0.76 1.7,1.7L21.2,4zM18.92,12c0.04,0.33 0.08,0.66 0.08,1 0,2.08 -0.8,3.97 -2.1,5.39 -0.26,-0.81 -1,-1.39 -1.9,-1.39h-1v-3c0,-0.55 -0.45,-1 -1,-1L7,13v-2h2c0.55,0 1,-0.45 1,-1L10,8h2c1.1,0 2,-0.9 2,-2L14,3.46c-0.95,-0.3 -1.95,-0.46 -3,-0.46C5.48,3 1,7.48 1,13s4.48,10 10,10 10,-4.48 10,-10c0,-0.34 -0.02,-0.67 -0.05,-1h-2.03zM10,20.93c-3.95,-0.49 -7,-3.85 -7,-7.93 0,-0.62 0.08,-1.21 0.21,-1.79L8,16v1c0,1.1 0.9,2 2,2v1.93z" />
index c801069..e3deaaa 100644 (file)
@@ -11,7 +11,7 @@
     android:autoMirrored="true"
     tools:ignore="VectorRaster" >
 
-    <!-- We have to use a hard coded color until API >= 21.  Then we can use `@color`. -->
+    <!-- A hard coded color must be used until API >= 21.  Then `@color` may be used. -->
     <path
         android:fillColor="#88000000"
         android:pathData="M22,4v-0.5C22,2.12 20.88,1 19.5,1S17,2.12 17,3.5L17,4c-0.55,0 -1,0.45 -1,1v4c0,0.55 0.45,1 1,1h5c0.55,0 1,-0.45 1,-1L23,5c0,-0.55 -0.45,-1 -1,-1zM21.2,4h-3.4v-0.5c0,-0.94 0.76,-1.7 1.7,-1.7s1.7,0.76 1.7,1.7L21.2,4zM18.92,12c0.04,0.33 0.08,0.66 0.08,1 0,2.08 -0.8,3.97 -2.1,5.39 -0.26,-0.81 -1,-1.39 -1.9,-1.39h-1v-3c0,-0.55 -0.45,-1 -1,-1L7,13v-2h2c0.55,0 1,-0.45 1,-1L10,8h2c1.1,0 2,-0.9 2,-2L14,3.46c-0.95,-0.3 -1.95,-0.46 -3,-0.46C5.48,3 1,7.48 1,13s4.48,10 10,10 10,-4.48 10,-10c0,-0.34 -0.02,-0.67 -0.05,-1h-2.03zM10,20.93c-3.95,-0.49 -7,-3.85 -7,-7.93 0,-0.62 0.08,-1.21 0.21,-1.79L8,16v1c0,1.1 0.9,2 2,2v1.93z" />
index ba20d43..930b9f4 100644 (file)
@@ -11,7 +11,7 @@
     android:autoMirrored="true"
     tools:ignore="VectorRaster" >
 
-    <!-- We have to use a hard coded color until API >= 21.  Then we can use `@color`. -->
+    <!-- A hard coded color must be used until API >= 21.  Then `@color` may be used. -->
     <path
         android:fillColor="#FF1E88E5"
         android:pathData="M22,4v-0.5C22,2.12 20.88,1 19.5,1S17,2.12 17,3.5L17,4c-0.55,0 -1,0.45 -1,1v4c0,0.55 0.45,1 1,1h5c0.55,0 1,-0.45 1,-1L23,5c0,-0.55 -0.45,-1 -1,-1zM21.2,4h-3.4v-0.5c0,-0.94 0.76,-1.7 1.7,-1.7s1.7,0.76 1.7,1.7L21.2,4zM18.92,12c0.04,0.33 0.08,0.66 0.08,1 0,2.08 -0.8,3.97 -2.1,5.39 -0.26,-0.81 -1,-1.39 -1.9,-1.39h-1v-3c0,-0.55 -0.45,-1 -1,-1L7,13v-2h2c0.55,0 1,-0.45 1,-1L10,8h2c1.1,0 2,-0.9 2,-2L14,3.46c-0.95,-0.3 -1.95,-0.46 -3,-0.46C5.48,3 1,7.48 1,13s4.48,10 10,10 10,-4.48 10,-10c0,-0.34 -0.02,-0.67 -0.05,-1h-2.03zM10,20.93c-3.95,-0.49 -7,-3.85 -7,-7.93 0,-0.62 0.08,-1.21 0.21,-1.79L8,16v1c0,1.1 0.9,2 2,2v1.93z" />
index 9fc00fe..ee0a73f 100644 (file)
@@ -11,7 +11,7 @@
     android:autoMirrored="true"
     tools:ignore="VectorRaster" >
 
-    <!-- We have to use a hard coded color until API >= 21.  Then we can use `@color`. -->
+    <!-- A hard coded color must be used until API >= 21.  Then `@color` may be used. -->
     <path
         android:fillColor="#FF1565C0"
         android:pathData="M22,4v-0.5C22,2.12 20.88,1 19.5,1S17,2.12 17,3.5L17,4c-0.55,0 -1,0.45 -1,1v4c0,0.55 0.45,1 1,1h5c0.55,0 1,-0.45 1,-1L23,5c0,-0.55 -0.45,-1 -1,-1zM21.2,4h-3.4v-0.5c0,-0.94 0.76,-1.7 1.7,-1.7s1.7,0.76 1.7,1.7L21.2,4zM18.92,12c0.04,0.33 0.08,0.66 0.08,1 0,2.08 -0.8,3.97 -2.1,5.39 -0.26,-0.81 -1,-1.39 -1.9,-1.39h-1v-3c0,-0.55 -0.45,-1 -1,-1L7,13v-2h2c0.55,0 1,-0.45 1,-1L10,8h2c1.1,0 2,-0.9 2,-2L14,3.46c-0.95,-0.3 -1.95,-0.46 -3,-0.46C5.48,3 1,7.48 1,13s4.48,10 10,10 10,-4.48 10,-10c0,-0.34 -0.02,-0.67 -0.05,-1h-2.03zM10,20.93c-3.95,-0.49 -7,-3.85 -7,-7.93 0,-0.62 0.08,-1.21 0.21,-1.79L8,16v1c0,1.1 0.9,2 2,2v1.93z" />
diff --git a/app/src/main/res/layout/http_authentication_dialog.xml b/app/src/main/res/layout/http_authentication_dialog.xml
new file mode 100644 (file)
index 0000000..df262c5
--- /dev/null
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  Copyright © 2017 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/>. -->
+
+<ScrollView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent" >
+
+    <LinearLayout
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:orientation="vertical"
+        android:layout_marginStart="4dp"
+        android:layout_marginEnd="4dp" >
+
+        <TextView
+            android:id="@+id/http_authentication_realm"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:layout_marginTop="12dp"
+            android:layout_gravity="center_horizontal"
+            android:textSize="24sp"
+            android:textStyle="bold" />
+
+        <TextView
+            android:id="@+id/http_authentication_host"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:layout_marginTop="6dp"
+            android:layout_marginBottom="6dp"
+            android:layout_marginStart="4dp"
+            android:layout_marginEnd="4dp" />
+
+        <!-- `android.support.design.widget.TextInputLayout` makes the `android:hint` float above the `EditText`. -->
+        <android.support.design.widget.TextInputLayout
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:layout_marginTop="6dp" >
+
+            <!-- `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.support.design.widget.TextInputEditText
+                android:id="@+id/http_authentication_username"
+                android:layout_height="wrap_content"
+                android:layout_width="match_parent"
+                android:hint="@string/username"
+                android:imeOptions="actionGo"
+                android:inputType="textUri"
+                android:selectAllOnFocus="true" />
+        </android.support.design.widget.TextInputLayout>
+
+        <!-- `android.support.design.widget.TextInputLayout` makes the `android:hint` float above the `EditText`. -->
+        <android.support.design.widget.TextInputLayout
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent" >
+
+            <!-- `android:imeOptions="actionGo"` sets the keyboard to have a `go` key instead of a `new line` key. -->
+            <EditText
+                android:id="@+id/http_authentication_password"
+                android:layout_height="wrap_content"
+                android:layout_width="match_parent"
+                android:hint="@string/password"
+                android:imeOptions="actionGo"
+                android:inputType="textPassword" />
+        </android.support.design.widget.TextInputLayout>
+    </LinearLayout>
+</ScrollView>
\ No newline at end of file
index ec86409..a76556d 100644 (file)
@@ -63,6 +63,8 @@
     <string name="no_ssl_certificate">La comunicación con esta página web no está cifrada. Esto permite a terceras partes interceptar información, rastrear su navegación e inyectar contenido malicioso.</string>
     <string name="ssl_certificate">Certificado SSL</string>
     <string name="close">Cerrar</string>
+    <string name="domain">Dominio</string>
+    <string name="domain_label">Dominio:</string>
     <string name="issued_to">Emitido a</string>
     <string name="issued_by">Emitido por</string>
     <string name="common_name">Nombre Común (CN):</string>
index 443ab4c..9ed001f 100644 (file)
@@ -65,6 +65,8 @@
     <string name="no_ssl_certificate">La comunicazione con questo sito web non è criptata. Questo consente a terze parti la possibilità di intercettare le informazioni scambiate, di tracciare la navigazione e di inviare contenuto maligno.</string>
     <string name="ssl_certificate">Certificato SSL</string>
     <string name="close">Chiudi</string>
+    <string name="domain">Dominio</string>
+    <string name="domain_label">Dominio:</string>
     <string name="issued_to">Emesso a</string>
     <string name="issued_by">Emesso da</string>
     <string name="common_name">Nome comune (NC):</string>
index cdd4531..c15727c 100644 (file)
     <string name="current_ssl">Current SSL</string>
     <string name="pinned_ssl">Pinned SSL</string>
 
+    <!-- HTTP Authentication. -->
+    <string name="http_authentication">HTTP Authentication</string>
+    <string name="host">Host:</string>
+    <string name="username">Username</string>
+    <string name="password">Password</string>
+
     <!-- MainWebViewActivity Navigation Drawer. -->
     <string name="navigation_drawer">Navigation Drawer</string>
     <string name="navigation">Navigation</string>