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