View SSL certificates and errors.
authorSoren Stoutner <soren@stoutner.com>
Fri, 26 Aug 2016 18:34:32 +0000 (11:34 -0700)
committerSoren Stoutner <soren@stoutner.com>
Fri, 26 Aug 2016 18:34:32 +0000 (11:34 -0700)
13 files changed:
.idea/dictionaries/soren.xml
app/src/main/assets/en/guide_clear_and_exit.html
app/src/main/java/com/stoutner/privacybrowser/BookmarksDatabaseHandler.java
app/src/main/java/com/stoutner/privacybrowser/CreateHomeScreenShortcut.java
app/src/main/java/com/stoutner/privacybrowser/MainWebViewActivity.java
app/src/main/java/com/stoutner/privacybrowser/SslCertificateError.java [new file with mode: 0644]
app/src/main/java/com/stoutner/privacybrowser/ViewSslCertificate.java [new file with mode: 0644]
app/src/main/res/layout/ssl_certificate_error.xml [new file with mode: 0644]
app/src/main/res/layout/unencrypted_website.xml [new file with mode: 0644]
app/src/main/res/layout/url_bar.xml
app/src/main/res/layout/view_ssl_certificate.xml [new file with mode: 0644]
app/src/main/res/menu/webview_options_menu.xml
app/src/main/res/values/strings.xml

index 24bc218d1bbbc5450d531230896b331437d64152..47992f9b2f57121ce67a86de363604fa3b0b5fd1 100644 (file)
       <w>checkedtextview</w>
       <w>chromebooks</w>
       <w>chromeversion</w>
+      <w>cname</w>
       <w>commitdiff</w>
       <w>coordinatorlayout</w>
       <w>displayorder</w>
+      <w>dname</w>
       <w>eadd</w>
       <w>edittext</w>
       <w>exynos</w>
@@ -33,6 +35,7 @@
       <w>lossless</w>
       <w>mozilla</w>
       <w>nojs</w>
+      <w>oname</w>
       <w>orbot</w>
       <w>panopticlick</w>
       <w>parentfolder</w>
@@ -54,6 +57,7 @@
       <w>techrepublic</w>
       <w>textview</w>
       <w>uids</w>
+      <w>uname</w>
       <w>webkay</w>
       <w>webkitversion</w>
       <w>whatismyip</w>
index c00ac623ae7438f93741685fb044af383b84c47d..1cab07965dd818835b849c9440c1f7c9e0ff78d6 100644 (file)
@@ -41,6 +41,7 @@
     <li><item>Removes all form data</item>.</li>
     <li><item>Clears the cache, including disk files</item>.</li>
     <li><item>Clears the back/forward history</item>.</li>
+    <li><item>Clears any saved SSL certificate preferences</item> (SSL certificates with errors that are ignored).</li>
     <li><item>Deletes the current URL</item>.</li>
     <li><item>Destroys the internal state of the WebView</item>.</li>
     <li><item>Closes Privacy Browser</item>. For Android Lollipop and newer (version >= 5.0 or API >= 21), Privacy Browser is also removed from the recent app list.</li>
index 2a81097efa507fbc22228e04ff44b3100eaeb64e..f03029a24ba8eaa2158db3d48e69941b8e59a6a5 100644 (file)
@@ -25,9 +25,6 @@ import android.database.Cursor;
 import android.database.DatabaseUtils;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteOpenHelper;
-import android.provider.ContactsContract;
-import android.support.design.widget.Snackbar;
-import android.support.v4.content.ContextCompat;
 
 public class BookmarksDatabaseHandler extends SQLiteOpenHelper {
     private static final int SCHEMA_VERSION = 1;
index 4a6b2fa7317768f04872dea018c60562f03fb0a1..35601387e5b097829a003dca7bc913f3915c23af 100644 (file)
@@ -42,7 +42,7 @@ public class CreateHomeScreenShortcut extends DialogFragment {
         void onCreateHomeScreenShortcut(DialogFragment dialogFragment);
     }
 
-    //createHomeScreenShortcutListener is used in onAttach and and onCreateDialog.
+    //createHomeScreenShortcutListener is used in `onAttach` and and `onCreateDialog`.
     private CreateHomeScreenSchortcutListener createHomeScreenShortcutListener;
 
     // Check to make sure that the parent activity implements the listener.
@@ -57,20 +57,20 @@ public class CreateHomeScreenShortcut extends DialogFragment {
 
     @Override
     public Dialog onCreateDialog(Bundle savedInstanceState) {
+        // Get the activity's layout inflater.
+        LayoutInflater layoutInflater = getActivity().getLayoutInflater();
+
         // Create a drawable version of the favorite icon.
         Drawable favoriteIconDrawable = new BitmapDrawable(getResources(), MainWebViewActivity.favoriteIcon);
 
-        // Get the activity's layout inflater.
-        LayoutInflater customDialogInflater = getActivity().getLayoutInflater();
-
-        // Use AlertDialog.Builder to create the AlertDialog.  The style formats the color of the button text.
+        // Use `AlertDialog.Builder` to create the `AlertDialog`.  `R.style.LightAlertDialog` formats the color of the button text.
         AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity(), R.style.LightAlertDialog);
         dialogBuilder.setTitle(R.string.create_shortcut);
         dialogBuilder.setIcon(favoriteIconDrawable);
-        // The parent view is "null" because it will be assigned by AlertDialog.
-        dialogBuilder.setView(customDialogInflater.inflate(R.layout.create_home_screen_shortcut_dialog, null));
+        // The parent view is `null` because it will be assigned by `AlertDialog`.
+        dialogBuilder.setView(layoutInflater.inflate(R.layout.create_home_screen_shortcut_dialog, null));
 
-        // Set an onClick listener on the negative button.
+        // Set an `onClick` listener on the negative button.
         dialogBuilder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
             @Override
             public void onClick(DialogInterface dialog, int which) {
@@ -78,7 +78,7 @@ public class CreateHomeScreenShortcut extends DialogFragment {
             }
         });
 
-        // Set an onClick listener on the positive button.
+        // Set an `onClick` listener on the positive button.
         dialogBuilder.setPositiveButton(R.string.create, new DialogInterface.OnClickListener() {
             @Override
             public void onClick(DialogInterface dialog, int which) {
@@ -87,13 +87,13 @@ public class CreateHomeScreenShortcut extends DialogFragment {
         });
 
 
-        // Create an AlertDialog from the AlertDialogBuilder.
+        // Create an `AlertDialog` from the `AlertDialog.Builder`.
         final AlertDialog alertDialog = dialogBuilder.create();
 
         // Show the keyboard when the Dialog 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.
+        // We need to show the `AlertDialog` before we can call setOnKeyListener() below.
         alertDialog.show();
 
         // Allow the "enter" key on the keyboard to create the shortcut.
@@ -117,7 +117,7 @@ public class CreateHomeScreenShortcut extends DialogFragment {
             }
         });
 
-        // onCreateDialog requires the return of an AlertDialog.
+        // `onCreateDialog` requires the return of an `AlertDialog`.
         return alertDialog;
     }
 }
\ No newline at end of file
index 4cad1873faf00f8c275ee0c1c8bf31d09214b406..0a824a1527c0f0b657ea51f84430f3ba7f916cf5 100644 (file)
@@ -30,6 +30,7 @@ import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
+import android.net.http.SslError;
 import android.os.Build;
 import android.os.Bundle;
 import android.preference.PreferenceManager;
@@ -51,6 +52,7 @@ import android.view.View;
 import android.view.inputmethod.InputMethodManager;
 import android.webkit.CookieManager;
 import android.webkit.DownloadListener;
+import android.webkit.SslErrorHandler;
 import android.webkit.WebChromeClient;
 import android.webkit.WebStorage;
 import android.webkit.WebView;
@@ -69,7 +71,7 @@ import java.util.HashMap;
 import java.util.Map;
 
 // 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, CreateHomeScreenShortcut.CreateHomeScreenSchortcutListener {
+public class MainWebViewActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener, CreateHomeScreenShortcut.CreateHomeScreenSchortcutListener, SslCertificateError.SslCertificateErrorListener {
     // `favoriteIcon` is public static so it can be accessed from `CreateHomeScreenShortcut`, `BookmarksActivity`, `CreateBookmark`, `CreateBookmarkFolder`, and `EditBookmark`.
     // It is also used in `onCreate()` and `onCreateHomeScreenShortcutCreate()`.
     public static Bitmap favoriteIcon;
@@ -140,6 +142,9 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
     // `adView` is used in `onCreate()` and `onConfigurationChanged()`.
     private View adView;
 
+    // `sslErrorHandler` is used in `onCreate()`, `onSslErrorCancel()`, and `onSslErrorProceed`.
+    private SslErrorHandler sslErrorHandler;
+
     @Override
     // Remove Android Studio's warning about the dangers of using SetJavaScriptEnabled.  The whole premise of Privacy Browser is built around an understanding of these dangers.
     @SuppressLint("SetJavaScriptEnabled")
@@ -251,6 +256,17 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
                     urlTextBox.setText(formattedUrlString);
                 }
             }
+
+            // Handle SSL Certificate errors.
+            @Override
+            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
+                // Store `handler` so it can be accesses from `onSslErrorCancel()` and `onSslErrorProceed()`.
+                sslErrorHandler = handler;
+
+                // Display the SSL error `AlertDialog`.
+                DialogFragment sslCertificateErrorDialogFragment = SslCertificateError.displayDialog(error);
+                sslCertificateErrorDialogFragment.show(getFragmentManager(), getResources().getString(R.string.ssl_certificate_error));
+            }
         });
 
         mainWebView.setWebChromeClient(new WebChromeClient() {
@@ -533,50 +549,57 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
         WebViewDatabase mainWebViewDatabase = WebViewDatabase.getInstance(this);
         clearFormData.setEnabled(mainWebViewDatabase.hasFormData());
 
-        // Select the current font size.
+        // Initialize font size variables.
         int fontSize = mainWebView.getSettings().getTextZoom();
-        MenuItem fontSizeMenuItem = menu.findItem(R.id.fontSize);
+        String fontSizeTitle;
         MenuItem selectedFontSizeMenuItem;
+
+        // Prepare the font size title and current size menu item.
         switch (fontSize) {
             case 50:
-                fontSizeMenuItem.setTitle(R.string.font_size_fifty_percent);
+                fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.fifty_percent);
                 selectedFontSizeMenuItem = menu.findItem(R.id.fontSizeFiftyPercent);
                 break;
 
             case 75:
-                fontSizeMenuItem.setTitle(R.string.font_size_seventy_five_percent);
+                fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.seventy_five_percent);
                 selectedFontSizeMenuItem = menu.findItem(R.id.fontSizeSeventyFivePercent);
                 break;
 
             case 100:
-                fontSizeMenuItem.setTitle(R.string.font_size_one_hundred_percent);
+                fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.one_hundred_percent);
                 selectedFontSizeMenuItem = menu.findItem(R.id.fontSizeOneHundredPercent);
                 break;
 
             case 125:
-                fontSizeMenuItem.setTitle(R.string.font_size_one_hundred_twenty_five_percent);
+                fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.one_hundred_twenty_five_percent);
                 selectedFontSizeMenuItem = menu.findItem(R.id.fontSizeOneHundredTwentyFivePercent);
                 break;
 
             case 150:
-                fontSizeMenuItem.setTitle(R.string.font_size_one_hundred_fifty_percent);
+                fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.one_hundred_fifty_percent);
                 selectedFontSizeMenuItem = menu.findItem(R.id.fontSizeOneHundredFiftyPercent);
                 break;
 
             case 175:
-                fontSizeMenuItem.setTitle(R.string.font_size_one_hundred_seventy_five_percent);
+                fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.one_hundred_seventy_five_percent);
                 selectedFontSizeMenuItem = menu.findItem(R.id.fontSizeOneHundredSeventyFivePercent);
                 break;
 
             case 200:
-                fontSizeMenuItem.setTitle(R.string.font_size_two_hundred_percent);
+                fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.two_hundred_percent);
                 selectedFontSizeMenuItem = menu.findItem(R.id.fontSizeTwoHundredPercent);
                 break;
 
             default:
+                fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.one_hundred_percent);
                 selectedFontSizeMenuItem = menu.findItem(R.id.fontSizeOneHundredPercent);
                 break;
         }
+
+        // Set the font size title and select the current size menu item.
+        MenuItem fontSizeMenuItem = menu.findItem(R.id.fontSize);
+        fontSizeMenuItem.setTitle(fontSizeTitle);
         selectedFontSizeMenuItem.setChecked(true);
 
         // Only show `Refresh` if `swipeToRefresh` is disabled.
@@ -744,7 +767,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
                 return true;
 
             case R.id.addToHomescreen:
-                // Show the CreateHomeScreenShortcut AlertDialog and name this instance "@string/create_shortcut".
+                // Show the `CreateHomeScreenShortcut` `AlertDialog` and name this instance `@string/create_shortcut`.
                 DialogFragment createHomeScreenShortcutDialogFragment = new CreateHomeScreenShortcut();
                 createHomeScreenShortcutDialogFragment.show(getFragmentManager(), getResources().getString(R.string.create_shortcut));
 
@@ -840,6 +863,10 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
                 // Clear the back/forward history.
                 mainWebView.clearHistory();
 
+                // Clear any SSL certificate preferences.
+                MainWebViewActivity.mainWebView.clearSslPreferences();
+
+                // Clear `formattedUrlString`.
                 formattedUrlString = null;
 
                 // Destroy the internal state of the webview.
@@ -905,6 +932,22 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation
         sendBroadcast(placeBookmarkShortcut);
     }
 
+    public void viewSslCertificate(View view) {
+        // Show the `ViewSslCertificate` `AlertDialog` and name this instance `@string/view_ssl_certificate`.
+        DialogFragment viewSslCertificateDialogFragment = new ViewSslCertificate();
+        viewSslCertificateDialogFragment.show(getFragmentManager(), getResources().getString(R.string.view_ssl_certificate));
+    }
+
+    @Override
+    public void onSslErrorCancel() {
+        sslErrorHandler.cancel();
+    }
+
+    @Override
+    public void onSslErrorProceed() {
+        sslErrorHandler.proceed();
+    }
+
     // Override onBackPressed to handle the navigation drawer and mainWebView.
     @Override
     public void onBackPressed() {
diff --git a/app/src/main/java/com/stoutner/privacybrowser/SslCertificateError.java b/app/src/main/java/com/stoutner/privacybrowser/SslCertificateError.java
new file mode 100644 (file)
index 0000000..80c9c1e
--- /dev/null
@@ -0,0 +1,249 @@
+/**
+ * Copyright 2015-2016 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;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.DialogInterface;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.http.SslCertificate;
+import android.net.http.SslError;
+import android.os.Bundle;
+import android.support.v4.content.ContextCompat;
+import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.style.ForegroundColorSpan;
+import android.view.LayoutInflater;
+import android.widget.TextView;
+
+import org.w3c.dom.Text;
+
+import java.util.Date;
+
+public class SslCertificateError extends DialogFragment{
+    private String primaryError;
+    private String urlWithError;
+    private String issuedToCName;
+    private String issuedToOName;
+    private String issuedToUName;
+    private String issuedByCName;
+    private String issuedByOName;
+    private String issuedByUName;
+    private String startDate;
+    private String endDate;
+
+    public static SslCertificateError displayDialog(SslError error) {
+        // Get the various components of the SSL error message.
+        int primaryErrorIntForBundle = error.getPrimaryError();
+        String urlWithErrorForBundle = error.getUrl();
+        SslCertificate sslCertificate = error.getCertificate();
+        String issuedToCNameForBundle = sslCertificate.getIssuedTo().getCName();
+        String issuedToONameForBundle = sslCertificate.getIssuedTo().getOName();
+        String issuedToUNameForBundle = sslCertificate.getIssuedTo().getUName();
+        String issuedByCNameForBundle = sslCertificate.getIssuedBy().getCName();
+        String issuedByONameForBundle = sslCertificate.getIssuedBy().getOName();
+        String issuedByUNameForBundle = sslCertificate.getIssuedBy().getUName();
+        Date startDateForBundle = sslCertificate.getValidNotBeforeDate();
+        Date endDateForBundle = sslCertificate.getValidNotAfterDate();
+
+        // Store the SSL error message components in a `Bundle`.
+        Bundle argumentsBundle = new Bundle();
+        argumentsBundle.putInt("PrimaryErrorInt", primaryErrorIntForBundle);
+        argumentsBundle.putString("UrlWithError", urlWithErrorForBundle);
+        argumentsBundle.putString("IssuedToCName", issuedToCNameForBundle);
+        argumentsBundle.putString("IssuedToOName", issuedToONameForBundle);
+        argumentsBundle.putString("IssuedToUName", issuedToUNameForBundle);
+        argumentsBundle.putString("IssuedByCName", issuedByCNameForBundle);
+        argumentsBundle.putString("IssuedByOName", issuedByONameForBundle);
+        argumentsBundle.putString("IssuedByUName", issuedByUNameForBundle);
+        argumentsBundle.putString("StartDate", startDateForBundle.toString());
+        argumentsBundle.putString("EndDate", endDateForBundle.toString());
+
+        // Add the `Bundle` to this instance of `SslCertificateError`.
+        SslCertificateError thisSslCertificateErrorDialog = new SslCertificateError();
+        thisSslCertificateErrorDialog.setArguments(argumentsBundle);
+        return thisSslCertificateErrorDialog;
+    }
+
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // Save the components of the SSL error message in class variables.
+        urlWithError = getArguments().getString("UrlWithError");
+        issuedToCName = getArguments().getString("IssuedToCName");
+        issuedToOName = getArguments().getString("IssuedToOName");
+        issuedToUName = getArguments().getString("IssuedToUName");
+        issuedByCName = getArguments().getString("IssuedByCName");
+        issuedByOName = getArguments().getString("IssuedByOName");
+        issuedByUName = getArguments().getString("IssuedByUName");
+        startDate = getArguments().getString("StartDate");
+        endDate = getArguments().getString("EndDate");
+
+        // Get the appropriate string for `primaryError.
+        int primaryErrorInt = getArguments().getInt("PrimaryErrorInt");
+        switch (primaryErrorInt) {
+            case SslError.SSL_NOTYETVALID:
+                primaryError = getString(R.string.future_certificate);
+                break;
+
+            case SslError.SSL_EXPIRED:
+                primaryError = getString(R.string.expired_certificate);
+                break;
+
+            case SslError.SSL_IDMISMATCH:
+                primaryError = getString(R.string.cn_mismatch);
+                break;
+
+            case SslError.SSL_UNTRUSTED:
+                primaryError = getString(R.string.untrusted);
+                break;
+
+            case SslError.SSL_DATE_INVALID:
+                primaryError = getString(R.string.invalid_date);
+                break;
+
+            case SslError.SSL_INVALID:
+                primaryError = getString(R.string.invalid_certificate);
+                break;
+        }
+    }
+
+    // The public interface is used to send information back to the parent activity.
+    public interface SslCertificateErrorListener {
+        void onSslErrorCancel();
+
+        void onSslErrorProceed();
+    }
+
+    // `sslCertificateErrorListener` is used in `onAttach` and `onCreateDialog`.
+    private SslCertificateErrorListener sslCertificateErrorListener;
+
+    // Check to make sure that the parent activity implements the listener.
+    public void onAttach(Activity parentActivity) {
+        super.onAttach(parentActivity);
+
+        try {
+            sslCertificateErrorListener = (SslCertificateErrorListener) parentActivity;
+        } catch(ClassCastException exception) {
+            throw new ClassCastException(parentActivity.toString() + " must implement SslCertificateErrorListener");
+        }
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        // Get the activity's layout inflater.
+        LayoutInflater layoutInflater = getActivity().getLayoutInflater();
+
+        // Use `AlertDialog.Builder` to create the `AlertDialog`.  `R.style.LightAlertDialog` formats the color of the button text.
+        AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity(), R.style.LightAlertDialog);
+        dialogBuilder.setTitle(R.string.ssl_certificate_error);
+        // The parent view is `null` because it will be assigned by `AlertDialog`.
+        dialogBuilder.setView(layoutInflater.inflate(R.layout.ssl_certificate_error, null));
+
+        // Set an `onClick` listener on the negative button.  `null` doesn't do anything extra when the button is pressed.  The `Dialog` will automatically close.
+        dialogBuilder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                sslCertificateErrorListener.onSslErrorCancel();
+            }
+        });
+
+        // Set an `onClick` listener on the positive button.
+        dialogBuilder.setPositiveButton(R.string.proceed, new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                sslCertificateErrorListener.onSslErrorProceed();
+            }
+        });
+
+
+        // Create an `AlertDialog` from the `AlertDialog.Builder`.
+        AlertDialog alertDialog = dialogBuilder.create();
+
+        // We have to show the `AlertDialog` before we can modify the content.
+        alertDialog.show();
+
+        // Get handles for the `TextViews`
+        TextView primaryErrorTextView = (TextView) alertDialog.findViewById(R.id.primary_error);
+        TextView urlTextView = (TextView) alertDialog.findViewById(R.id.url_error_dialog);
+        TextView issuedToCNameTextView = (TextView) alertDialog.findViewById(R.id.issued_to_cname_error_dialog);
+        TextView issuedToONameTextView = (TextView) alertDialog.findViewById(R.id.issued_to_oname_error_dialog);
+        TextView issuedToUNameTextView = (TextView) alertDialog.findViewById(R.id.issued_to_uname_error_dialog);
+        TextView issuedByCNameTextView = (TextView) alertDialog.findViewById(R.id.issued_by_cname_error_dialog);
+        TextView issuedByONameTextView = (TextView) alertDialog.findViewById(R.id.issued_by_oname_error_dialog);
+        TextView issuedByUNameTextView = (TextView) alertDialog.findViewById(R.id.issued_by_uname_error_dialog);
+        TextView startDateTextView = (TextView) alertDialog.findViewById(R.id.start_date_error_dialog);
+        TextView endDateTextView = (TextView) alertDialog.findViewById(R.id.end_date_error_dialog);
+
+        // Setup the common strings.
+        String urlLabel = getString(R.string.url_label) + "  ";
+        String cNameLabel = getString(R.string.common_name) + "  ";
+        String oNameLabel = getString(R.string.organization) + "  ";
+        String uNameLabel = getString(R.string.organizational_unit) + "  ";
+        String startDateLabel = getString(R.string.start_date) + "  ";
+        String endDateLabel = getString(R.string.end_date) + "  ";
+
+        // Create a `SpannableStringBuilder` for each item.
+        SpannableStringBuilder urlStringBuilder = new SpannableStringBuilder(urlLabel + urlWithError);
+        SpannableStringBuilder issuedToCNameStringBuilder = new SpannableStringBuilder(cNameLabel + issuedToCName);
+        SpannableStringBuilder issuedToONameStringBuilder = new SpannableStringBuilder(oNameLabel + issuedToOName);
+        SpannableStringBuilder issuedToUNameStringBuilder = new SpannableStringBuilder(uNameLabel + issuedToUName);
+        SpannableStringBuilder issuedByCNameStringBuilder = new SpannableStringBuilder(cNameLabel + issuedByCName);
+        SpannableStringBuilder issuedByONameStringBuilder = new SpannableStringBuilder(oNameLabel + issuedByOName);
+        SpannableStringBuilder issuedByUNameStringBuilder = new SpannableStringBuilder(uNameLabel + issuedByUName);
+        SpannableStringBuilder startDateStringBuilder = new SpannableStringBuilder(startDateLabel + startDate);
+        SpannableStringBuilder endDateStringBuilder = new SpannableStringBuilder((endDateLabel + endDate));
+
+        // Create a blue `ForegroundColorSpan`.  We have to use the deprecated `getColor` until API >= 23.
+        ForegroundColorSpan blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.blue));
+
+        // Setup the spans to display the certificate information in blue.
+        // `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
+        urlStringBuilder.setSpan(blueColorSpan, urlLabel.length(), urlStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+        issuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), issuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+        issuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), issuedToONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+        issuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), issuedToUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+        issuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), issuedByCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+        issuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), issuedByONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+        issuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), issuedByUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+        startDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length(), startDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+        endDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length(), endDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+
+
+        // Display the strings.
+        primaryErrorTextView.setText(primaryError);
+        urlTextView.setText(urlStringBuilder);
+        issuedToCNameTextView.setText(issuedToCNameStringBuilder);
+        issuedToONameTextView.setText(issuedToONameStringBuilder);
+        issuedToUNameTextView.setText(issuedToUNameStringBuilder);
+        issuedByCNameTextView.setText(issuedByCNameStringBuilder);
+        issuedByONameTextView.setText(issuedByONameStringBuilder);
+        issuedByUNameTextView.setText(issuedByUNameStringBuilder);
+        startDateTextView.setText(startDateStringBuilder);
+        endDateTextView.setText(endDateStringBuilder);
+
+        // `onCreateDialog` requires the return of an `AlertDialog`.
+        return alertDialog;
+    }
+}
diff --git a/app/src/main/java/com/stoutner/privacybrowser/ViewSslCertificate.java b/app/src/main/java/com/stoutner/privacybrowser/ViewSslCertificate.java
new file mode 100644 (file)
index 0000000..8b37074
--- /dev/null
@@ -0,0 +1,153 @@
+/**
+ * Copyright 2016 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;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.http.SslCertificate;
+import android.os.Bundle;
+import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.style.ForegroundColorSpan;
+import android.view.LayoutInflater;
+import android.widget.TextView;
+
+import org.w3c.dom.Text;
+
+import java.util.Date;
+
+public class ViewSslCertificate extends DialogFragment {
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        // Get the activity's layout inflater.
+        LayoutInflater layoutInflater   = getActivity().getLayoutInflater();
+
+        // Create a drawable version of the favorite icon.
+        Drawable favoriteIconDrawable = new BitmapDrawable(getResources(), MainWebViewActivity.favoriteIcon);
+
+        // Use `AlertDialog.Builder` to create the `AlertDialog`.  `R.style.LightAlertDialog` formats the color of the button text.
+        AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity(), R.style.LightAlertDialog);
+        dialogBuilder.setIcon(favoriteIconDrawable);
+
+        // Set an `onClick` listener on the negative button.  Using `null` closes the dialog without doing anything else.
+        dialogBuilder.setNegativeButton(R.string.close, null);
+
+        // Check to see if the website is encrypted.
+        if (MainWebViewActivity.mainWebView.getCertificate() == null) {  // The website is not encrypted.
+            // Set the title.
+            dialogBuilder.setTitle(R.string.unencrypted_website);
+
+            // Set the Layout.  The parent view is `null` because it will be assigned by `AlertDialog`.
+            dialogBuilder.setView(layoutInflater.inflate(R.layout.unencrypted_website, null));
+
+            // Create an `AlertDialog` from the `AlertDialog.Builder`
+            final AlertDialog alertDialog = dialogBuilder.create();
+
+            // Show `alertDialog`.
+            alertDialog.show();
+
+            // `onCreateDialog` requires the return of an `AlertDialog`.
+            return alertDialog;
+
+        } else {  // Display the SSL certificate information
+            // Set the title.
+            dialogBuilder.setTitle(R.string.ssl_certificate);
+
+            // Set the layout.  The parent view is `null` because it will be assigned by `AlertDialog`.
+            dialogBuilder.setView(layoutInflater.inflate(R.layout.view_ssl_certificate, null));
+
+            // Create an `AlertDialog` from the `AlertDialog.Builder`
+            final AlertDialog alertDialog = dialogBuilder.create();
+
+            // We need to show the `AlertDialog` before we can modify items in the layout.
+            alertDialog.show();
+
+            // Get handles for the `TextViews`.
+            TextView issuedToCNameTextView = (TextView) alertDialog.findViewById(R.id.issued_to_cname);
+            TextView issuedToONameTextView = (TextView) alertDialog.findViewById(R.id.issued_to_oname);
+            TextView issuedToUNameTextView = (TextView) alertDialog.findViewById(R.id.issued_to_uname);
+            TextView issuedByCNameTextView = (TextView) alertDialog.findViewById(R.id.issued_by_cname);
+            TextView issuedByONameTextView = (TextView) alertDialog.findViewById(R.id.issued_by_oname);
+            TextView issuedByUNameTextView = (TextView) alertDialog.findViewById(R.id.issued_by_uname);
+            TextView startDateTextView = (TextView) alertDialog.findViewById(R.id.start_date);
+            TextView endDateTextView = (TextView) alertDialog.findViewById(R.id.end_date);
+
+            // Setup the labels.
+            String cNameLabel = getString(R.string.common_name) + "  ";
+            String oNameLabel = getString(R.string.organization) + "  ";
+            String uNameLabel = getString(R.string.organizational_unit) + "  ";
+            String startDateLabel = getString(R.string.start_date) + "  ";
+            String endDateLabel = getString(R.string.end_date) + "  ";
+
+            // Get the SSL certificate.
+            SslCertificate sslCertificate = MainWebViewActivity.mainWebView.getCertificate();
+
+            // Get the strings from the SSL certificate.
+            String issuedToCNameString = sslCertificate.getIssuedTo().getCName();
+            String issuedToONameString = sslCertificate.getIssuedTo().getOName();
+            String issuedToUNameString = sslCertificate.getIssuedTo().getUName();
+            String issuedByCNameString = sslCertificate.getIssuedBy().getCName();
+            String issuedByONameString = sslCertificate.getIssuedBy().getOName();
+            String issuedByUNameString = sslCertificate.getIssuedBy().getUName();
+            Date startDate = sslCertificate.getValidNotBeforeDate();
+            Date endDate = sslCertificate.getValidNotAfterDate();
+
+            // Create a `SpannableStringBuilder` for each item.
+            SpannableStringBuilder issuedToCNameStringBuilder = new SpannableStringBuilder(cNameLabel + issuedToCNameString);
+            SpannableStringBuilder issuedToONameStringBuilder = new SpannableStringBuilder(oNameLabel + issuedToONameString);
+            SpannableStringBuilder issuedToUNameStringBuilder = new SpannableStringBuilder(uNameLabel + issuedToUNameString);
+            SpannableStringBuilder issuedByCNameStringBuilder = new SpannableStringBuilder(cNameLabel + issuedByCNameString);
+            SpannableStringBuilder issuedByONameStringBuilder = new SpannableStringBuilder(oNameLabel + issuedByONameString);
+            SpannableStringBuilder issuedByUNameStringBuilder = new SpannableStringBuilder(uNameLabel + issuedByUNameString);
+            SpannableStringBuilder startDateStringBuilder = new SpannableStringBuilder(startDateLabel + startDate.toString());
+            SpannableStringBuilder endDateStringBuilder = new SpannableStringBuilder(endDateLabel + endDate.toString());
+
+            // Create a blue `ForegroundColorSpan`.  We have to use the deprecated `getColor` until API >= 23.
+            ForegroundColorSpan blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.blue));
+
+            // Setup the spans to display the certificate information in blue.
+            // `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
+            issuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), issuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+            issuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), issuedToONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+            issuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), issuedToUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+            issuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), issuedByCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+            issuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), issuedByONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+            issuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), issuedByUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+            startDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length(), startDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+            endDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length(), endDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+
+            // Display the strings.
+            issuedToCNameTextView.setText(issuedToCNameStringBuilder);
+            issuedToONameTextView.setText(issuedToONameStringBuilder);
+            issuedToUNameTextView.setText(issuedToUNameStringBuilder);
+            issuedByCNameTextView.setText(issuedByCNameStringBuilder);
+            issuedByONameTextView.setText(issuedByONameStringBuilder);
+            issuedByUNameTextView.setText(issuedByUNameStringBuilder);
+            startDateTextView.setText(startDateStringBuilder);
+            endDateTextView.setText(endDateStringBuilder);
+
+            // `onCreateDialog` requires the return of an `AlertDialog`.
+            return alertDialog;
+        }
+    }
+}
diff --git a/app/src/main/res/layout/ssl_certificate_error.xml b/app/src/main/res/layout/ssl_certificate_error.xml
new file mode 100644 (file)
index 0000000..c962651
--- /dev/null
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  Copyright 2016 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="wrap_content" >
+
+    <LinearLayout
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:padding="10dp"
+        android:orientation="vertical" >
+
+        <TextView
+            android:id="@+id/primary_error"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:textColor="@color/red"
+            android:textStyle="bold"/>
+
+        <!-- URL. -->
+        <TextView
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:layout_marginTop="15dp"
+            android:text="@string/url"
+            android:textAllCaps="true"
+            android:textStyle="bold"
+            android:textColor="@color/dark_blue" />
+
+        <TextView
+            android:id="@+id/url_error_dialog"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content" />
+
+        <!-- Issued To. -->
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="15dp"
+            android:text="@string/issued_to"
+            android:textAllCaps="true"
+            android:textStyle="bold"
+            android:textColor="@color/dark_blue" />
+
+        <TextView
+            android:id="@+id/issued_to_cname_error_dialog"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content" />
+
+        <TextView
+            android:id="@+id/issued_to_oname_error_dialog"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content" />
+
+        <TextView
+            android:id="@+id/issued_to_uname_error_dialog"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"/>
+
+
+        <!-- Issued By. -->
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="15dp"
+            android:text="@string/issued_by"
+            android:textAllCaps="true"
+            android:textStyle="bold"
+            android:textColor="@color/dark_blue"/>
+
+        <TextView
+            android:id="@+id/issued_by_cname_error_dialog"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+
+        <TextView
+            android:id="@+id/issued_by_oname_error_dialog"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content" />
+
+        <TextView
+            android:id="@+id/issued_by_uname_error_dialog"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content" />
+
+
+        <!-- Valid Dates. -->
+        <TextView
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:layout_marginTop="15dp"
+            android:text="@string/valid_dates"
+            android:textAllCaps="true"
+            android:textStyle="bold"
+            android:textColor="@color/dark_blue"/>
+
+        <TextView
+            android:id="@+id/start_date_error_dialog"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content" />
+
+        <TextView
+            android:id="@+id/end_date_error_dialog"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content" />
+    </LinearLayout>
+</ScrollView>
\ No newline at end of file
diff --git a/app/src/main/res/layout/unencrypted_website.xml b/app/src/main/res/layout/unencrypted_website.xml
new file mode 100644 (file)
index 0000000..88b2709
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  Copyright 2016 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/>. -->
+
+<TextView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent"
+    android:padding="10dp"
+    android:text="@string/no_ssl_certificate" />
\ No newline at end of file
index 4ee8cbdaf5aec342f92bcc7a9c4ece155e9e90d9..19751aa510b4b95d9f581a0ac26d624718068b15 100644 (file)
@@ -39,7 +39,8 @@
             android:layout_height="26dp"
             android:layout_width="26dp"
             android:layout_centerVertical="true"
-            android:contentDescription="@string/favorite_icon"/>
+            android:onClick="viewSslCertificate"
+            android:contentDescription="@string/favorite_icon" />
 
         <!-- `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.
diff --git a/app/src/main/res/layout/view_ssl_certificate.xml b/app/src/main/res/layout/view_ssl_certificate.xml
new file mode 100644 (file)
index 0000000..cdb5ceb
--- /dev/null
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  Copyright 2016 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="wrap_content" >
+
+    <LinearLayout
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:padding="10dp"
+        android:orientation="vertical" >
+
+        <!-- Issued To. -->
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/issued_to"
+            android:textAllCaps="true"
+            android:textStyle="bold"
+            android:textColor="@color/dark_blue" />
+
+        <TextView
+            android:id="@+id/issued_to_cname"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content" />
+
+        <TextView
+            android:id="@+id/issued_to_oname"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content" />
+
+        <TextView
+            android:id="@+id/issued_to_uname"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"/>
+
+
+        <!-- Issued By. -->
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="15dp"
+            android:text="@string/issued_by"
+            android:textAllCaps="true"
+            android:textStyle="bold"
+            android:textColor="@color/dark_blue"/>
+
+        <TextView
+            android:id="@+id/issued_by_cname"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+
+        <TextView
+            android:id="@+id/issued_by_oname"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content" />
+
+        <TextView
+            android:id="@+id/issued_by_uname"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content" />
+
+
+        <!-- Valid Dates. -->
+        <TextView
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:layout_marginTop="15dp"
+            android:text="@string/valid_dates"
+            android:textAllCaps="true"
+            android:textStyle="bold"
+            android:textColor="@color/dark_blue"/>
+
+        <TextView
+            android:id="@+id/start_date"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content" />
+
+        <TextView
+            android:id="@+id/end_date"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content" />
+    </LinearLayout>
+</ScrollView>
\ No newline at end of file
index 9c189886da6205339f01437e020f5453783a5f3a..202437d1cee8ef298c5dbe42ccc53a81c108fff3 100644 (file)
@@ -78,7 +78,7 @@
 
     <item
         android:id="@+id/fontSize"
-        android:title="@string/font_size_one_hundred_percent"
+        android:title="@string/font_size"
         android:orderInCategory="90"
         app:showAsAction="never" >
 
             <group android:checkableBehavior="single">
                 <item
                     android:id="@+id/fontSizeFiftyPercent"
-                    android:title="@string/font_size_fifty_percent"
+                    android:title="@string/fifty_percent"
                     android:orderInCategory="91"
                     app:showAsAction="never" />
 
                 <item
                     android:id="@+id/fontSizeSeventyFivePercent"
-                    android:title="@string/font_size_seventy_five_percent"
+                    android:title="@string/seventy_five_percent"
                     android:orderInCategory="92"
                     app:showAsAction="never" />
 
                 <item
                     android:id="@+id/fontSizeOneHundredPercent"
-                    android:title="@string/font_size_one_hundred_percent"
+                    android:title="@string/one_hundred_percent"
                     android:orderInCategory="93"
                     app:showAsAction="never" />
 
                 <item
                     android:id="@+id/fontSizeOneHundredTwentyFivePercent"
-                    android:title="@string/font_size_one_hundred_twenty_five_percent"
+                    android:title="@string/one_hundred_twenty_five_percent"
                     android:orderInCategory="94"
                     app:showAsAction="never" />
 
                 <item
                     android:id="@+id/fontSizeOneHundredFiftyPercent"
-                    android:title="@string/font_size_one_hundred_fifty_percent"
+                    android:title="@string/one_hundred_fifty_percent"
                     android:orderInCategory="95"
                     app:showAsAction="never" />
 
                 <item
                     android:id="@+id/fontSizeOneHundredSeventyFivePercent"
-                    android:title="@string/font_size_one_hundred_seventy_five_percent"
+                    android:title="@string/one_hundred_seventy_five_percent"
                     android:orderInCategory="96"
                     app:showAsAction="never" />
 
                 <item
                     android:id="@+id/fontSizeTwoHundredPercent"
-                    android:title="@string/font_size_two_hundred_percent"
+                    android:title="@string/two_hundred_percent"
                     android:orderInCategory="97"
                     app:showAsAction="never" />
             </group>
index 977d412d92705ca86c3354d4504a3a64e349c94c..ac11edcfc80a2e6fcf97c157b364a39cb261e9fa 100644 (file)
     <string name="favorite_icon">Favorite Icon</string>
     <string name="url_or_search_terms">URL or Search Terms</string>
 
+    <!-- View SSL Certificate. -->
+    <string name="view_ssl_certificate">View SSL Certificate</string>
+    <string name="unencrypted_website">Unencrypted Website</string>
+    <string name="no_ssl_certificate">Communicated with this website is not encrypted by an SSL certificate.</string>
+    <string name="ssl_certificate">SSL Certificate</string>
+    <string name="close">Close</string>
+    <string name="issued_to">Issued To</string>
+    <string name="issued_by">Issued By</string>
+    <string name="common_name">Common Name (CN):</string>
+    <string name="organization">Organization (O):</string>
+    <string name="organizational_unit">Organizational Unit (OU):</string>
+    <string name="valid_dates">Valid Dates</string>
+    <string name="start_date">Start Date:</string>
+    <string name="end_date">End Date:</string>
+
+    <!-- SSL Certificate Error. -->
+    <string name="ssl_certificate_error">SSL Certificate Error</string>
+    <string name="proceed">Proceed</string>
+    <string name="future_certificate">The certificate start date is in the future</string>
+    <string name="expired_certificate">The certificate is expired</string>
+    <string name="cn_mismatch">The Common Name does not match the hostname</string>
+    <string name="untrusted">The certificate authority is not trusted</string>
+    <string name="invalid_date">The date on the certificate is invalid</string>
+    <string name="invalid_certificate">The certificate is invalid</string>
+    <string name="url">URL</string>
+    <string name="url_label">URL:</string>
+
     <!-- Main `WebView` Navigation Drawer. -->
     <string name="navigation_drawer">Navigation Drawer</string>
     <string name="navigation">Navigation</string>
     <string name="clear_cookies">Clear Cookies</string>
     <string name="clear_dom_storage">Clear DOM Storage</string>
     <string name="clear_form_data">Clear Form Data</string>
-        <string name="font_size_fifty_percent">Font Size 50%</string>
-        <string name="font_size_seventy_five_percent">Font Size 75%</string>
-        <string name="font_size_one_hundred_percent">Font Size 100%</string>
-        <string name="font_size_one_hundred_twenty_five_percent">Font Size 125%</string>
-        <string name="font_size_one_hundred_fifty_percent">Font Size 150%</string>
-        <string name="font_size_one_hundred_seventy_five_percent">Font Size 175%</string>
-        <string name="font_size_two_hundred_percent">Font Size 200%</string>
+    <string name="font_size">Font Size</string>
+        <string name="fifty_percent">50%</string>
+        <string name="seventy_five_percent">75%</string>
+        <string name="one_hundred_percent">100%</string>
+        <string name="one_hundred_twenty_five_percent">125%</string>
+        <string name="one_hundred_fifty_percent">150%</string>
+        <string name="one_hundred_seventy_five_percent">175%</string>
+        <string name="two_hundred_percent">200%</string>
     <string name="share">Share</string>
     <string name="add_to_home_screen">Add to Home Screen</string>
     <string name="refresh">Refresh</string>