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 f6ab929f06f0eaf89ae2c391444f29377097ebfe..25a080151ea7f7da6ab00141f21e972bb9bf7c6f 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 7899ba461c4629a01ff3c68a426f97b2a7a14048..23c09ef915b29a596f87f68a01069d35fd16c8bf 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 cad98dc9e14de833c5092d1b4752ad46cac21282..295123d575c78833cf9aaaa3483c658ccc3e44fb 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 55df760e85da9f6ea60ff331638700fdb33aae20..633afd4e70f06f8e6eca0af0b59bcf5642806f40 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 f32e67d9d185a2e76aad7d0537713abbd19f18a0..813eb5fb892c71e0b2fe0811531dc62a2c65165f 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 23ee075afbe35e182746f14a672c9cc489c6ac21..7e3899679620b5c17cec9564b316ea9cbf4d2bb3 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 e16901d0baa0cfb02bb7d2c8aca10ecac7b9f7e8..b13a785235c96e6d72208d92d0fc794e17140714 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 60863a9c9d3c430e15a1fd99aedde5d178ecf563..b2966c14a4fb9fd067c22a55aac95a3c6b6ccfee 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 aa5b6279d8ae2b9e670bdaa094c0ef7f06f4edd0..d3315ad126b7e9e6ca15676c34f070591655524d 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 e966b2ecfd74fb7a8fd8b6a78207f9d05ff1a14b..00a3b1f915ecb3464d8970c48923e6f5627ada77 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 16dc955b4eab9e2034722f893c14688789a28270..b893e40245d53d62a14216e99a78ac057cceb777 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 92e9b1974d5b12c0d60647c765ab670999d5aedf..98ec8c2dfb3b54f9c8fa036cea6f83b6588d1328 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 3f6998c1601b1217c1afd9abf5841d3423e1c9bc..425c3720b83d05e3192d4eb883bdce44128da8e1 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 c801069733ec9fe121e421e4121f3f98d2191e3e..e3deaaab9e7ca392222708cb57af6f3c46a0bb7c 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 ba20d43103eab01710c7d57e4d7bfe39adc41330..930b9f4713b2e9f98544fedc1f46ce897fae57fc 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 9fc00fe8edbf5fa24f6139137fa8d1bfa27f7408..ee0a73fc7755e22d039e134a3b6d4f4e8d7ee855 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 ec864095e394c976a8c1569e79276f68057833f3..a76556dc8de4b44001fe5784b70852aee0cbbe3d 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 443ab4cd65368ee33bc319402fe282b7b5606b6c..9ed001fd9013cbaacc956c206daf410df4e70778 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 cdd45310532f614c80a906e09b6221a3a7faa107..c15727c53d8a02089f2d8aca5d261e1ff976cf97 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>