<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>
<w>lossless</w>
<w>mozilla</w>
<w>nojs</w>
+ <w>oname</w>
<w>orbot</w>
<w>panopticlick</w>
<w>parentfolder</w>
<w>techrepublic</w>
<w>textview</w>
<w>uids</w>
+ <w>uname</w>
<w>webkay</w>
<w>webkitversion</w>
<w>whatismyip</w>
<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>
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;
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.
@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) {
}
});
- // 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) {
});
- // 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.
}
});
- // onCreateDialog requires the return of an AlertDialog.
+ // `onCreateDialog` requires the return of an `AlertDialog`.
return alertDialog;
}
}
\ No newline at end of file
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;
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;
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;
// `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")
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() {
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.
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));
// 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.
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() {
--- /dev/null
+/**
+ * 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;
+ }
+}
--- /dev/null
+/**
+ * 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;
+ }
+ }
+}
--- /dev/null
+<?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
--- /dev/null
+<?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
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.
--- /dev/null
+<?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
<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>
<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>