import android.content.res.Configuration;
import android.database.Cursor;
import android.graphics.Bitmap;
+import android.graphics.Typeface;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.support.v7.app.AppCompatDialogFragment;
import android.support.v7.widget.Toolbar;
import android.text.Editable;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
import android.text.TextWatcher;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.StyleSpan;
import android.util.Patterns;
import android.view.ContextMenu;
import android.view.GestureDetector;
// `webViewTitle` is public static so it can be accessed from `CreateBookmarkDialog` and `CreateHomeScreenShortcutDialog`. It is also used in `onCreate()`.
public static String webViewTitle;
- // `displayWebpageImagesBoolean` is public static so it can be accessed from `DomainSettingsFragment`. It is also used in `applyAppSettings` and `applyDomainSettings()`.
+ // `displayWebpageImagesBoolean` is public static so it can be accessed from `DomainSettingsFragment`. It is also used in `applyAppSettings()` and `applyDomainSettings()`.
public static boolean displayWebpageImagesBoolean;
+ // `reloadOnRestartBoolean` is public static so it can be accessed from `SettingsFragment`. It is also used in `onRestart()`
+ public static boolean reloadOnRestartBoolean;
+
// `navigatingHistory` is used in `onCreate()`, `onNavigationItemSelected()`, and `applyDomainSettings()`.
private boolean navigatingHistory;
// `onTheFlyDisplayImagesSet` is used in `applyDomainSettings()` and `setDisplayWebpageImages()`.
private boolean onTheFlyDisplayImagesSet;
- // `loadingNewIntentBoolean` is used in `onNewIntent()` and `onRestart()`.
- private boolean loadingNewIntentBoolean;
-
// `waitingForOrbotData` is used in `onCreate()` and `applyAppSettings()`.
private String waitingForOrbotHTMLString;
appBar.setCustomView(R.layout.url_app_bar);
appBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
- // Set the "go" button on the keyboard to load the URL in urlTextBox.
+ // Create a `ForegroundColorSpan` and `StyleSpan` for formatting of `urlTextBox`. We have to use the deprecated `getColor()` until API >= 23.
+ @SuppressWarnings("deprecation") final ForegroundColorSpan redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700));
+ final StyleSpan boldStyleSpan = new StyleSpan(Typeface.BOLD);
+
+ // Get a handle for `urlTextBox`.
urlTextBox = (EditText) appBar.getCustomView().findViewById(R.id.url_edittext);
+
+ // Remove the formatting from `urlTextBar` when the user is editing the text.
+ urlTextBox.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ if (hasFocus) { // The user is editing `urlTextBox`.
+ // Remove the formatting.
+ urlTextBox.getText().removeSpan(redColorSpan);
+ urlTextBox.getText().removeSpan(boldStyleSpan);
+ } else { // The user has stopped editing `urlTextBox`.
+ // Highlight connections that are not encrypted.
+ if (urlTextBox.getText().toString().startsWith("http://")) {
+ urlTextBox.getText().setSpan(redColorSpan, 0, 7, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+ urlTextBox.getText().setSpan(boldStyleSpan, 0, 7, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+ }
+ }
+ }
+ });
+
+ // Set the `Go` button on the keyboard to load the URL in `urlTextBox`.
urlTextBox.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
// Hide the keyboard so we can see the navigation menu. `0` indicates no additional flags.
inputMethodManager.hideSoftInputFromWindow(mainWebView.getWindowToken(), 0);
+
+ // Clear the focus from `urlTextBox` if it has it.
+ urlTextBox.clearFocus();
}
}
});
// We need to update `formattedUrlString` at the beginning of the load, so that if the user toggles JavaScript during the load the new website is reloaded.
formattedUrlString = url;
- // Display the loading URL is the URL text box.
- urlTextBox.setText(url);
+ // Setup a `formattedUrlStringBuilder` to format the text in `urlTextBox`.
+ SpannableStringBuilder formattedUrlStringBuilder = new SpannableStringBuilder(formattedUrlString);
+
+ // Highlight connections that are not encrypted.
+ if (formattedUrlString.startsWith("http://")) {
+ formattedUrlStringBuilder.setSpan(redColorSpan, 0, 7, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+ formattedUrlStringBuilder.setSpan(boldStyleSpan, 0, 7, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+ }
+
+ // Display the formatted URL text.
+ urlTextBox.setText(formattedUrlStringBuilder);
// Apply any custom domain settings if the URL was loaded by navigating history.
if (navigatingHistory) {
// Set `formattedUrlString` to `""`.
formattedUrlString = "";
- // Update `urlTextBox`.
urlTextBox.setText(formattedUrlString);
// Request focus for `urlTextBox`.
// Display the keyboard.
inputMethodManager.showSoftInput(urlTextBox, 0);
- // Apply the domain settings.
+ // Apply the domain settings. This clears any settings from the previous domain.
applyDomainSettings(formattedUrlString);
} else { // `WebView` has loaded a webpage.
// Set `formattedUrlString`.
// Only update `urlTextBox` if the user is not typing in it.
if (!urlTextBox.hasFocus()) {
- urlTextBox.setText(formattedUrlString);
+ // Setup a `formattedUrlStringBuilder` to format the text in `urlTextBox`.
+ SpannableStringBuilder formattedUrlStringBuilder = new SpannableStringBuilder(formattedUrlString);
+
+ // Highlight connections that are not encrypted.
+ if (formattedUrlString.startsWith("http://")) {
+ formattedUrlStringBuilder.setSpan(redColorSpan, 0, 7, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+ formattedUrlStringBuilder.setSpan(boldStyleSpan, 0, 7, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+ }
+
+ // Display the formatted URL text.
+ urlTextBox.setText(formattedUrlStringBuilder);
}
}
@Override
protected void onNewIntent(Intent intent) {
- // Set `loadingNewIntentBoolean`.
- loadingNewIntentBoolean = true;
-
// Sets the new intent as the activity intent, so that any future `getIntent()`s pick up this one instead of creating a new activity.
setIntent(intent);
// Set the display webpage images mode.
setDisplayWebpageImages();
- // Only reload `mainWebView` if not loading a new intent and not waiting for Orbot.
- if (!loadingNewIntentBoolean && !waitingForOrbot) {
- // Reload the webpage to remove images if `setDisplayWebpageImages` has turned them off.
+ // Reload the webpage if displaying of images has been disabled in `SettingsFragment`.
+ if (reloadOnRestartBoolean) {
+ // Reload `mainWebView`.
mainWebView.reload();
- } else if (loadingNewIntentBoolean) { // Reset `loadingNewIntentBoolean` if this run comes from a new intent.
- loadingNewIntentBoolean = false;
+
+ // Reset `reloadOnRestartBoolean`.
+ reloadOnRestartBoolean = false;
}
}
break;
case R.id.clearAndExit:
- // Clear cookies. The commands changed slightly in API 21.
- if (Build.VERSION.SDK_INT >= 21) {
- cookieManager.removeAllCookies(null);
- } else {
- cookieManager.removeAllCookie();
+ // Get a handle for `sharedPreferences`. `this` references the current context.
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+
+ boolean clearEverything = sharedPreferences.getBoolean("clear_everything", true);
+
+ // Clear cookies.
+ if (clearEverything || sharedPreferences.getBoolean("clear_cookies", true)) {
+ // The command to remove cookies changed slightly in API 21.
+ if (Build.VERSION.SDK_INT >= 21) {
+ cookieManager.removeAllCookies(null);
+ } else {
+ cookieManager.removeAllCookie();
+ }
+
+ // Manually delete the cookies database, as `CookieManager` sometimes will not flush its changes to disk before `System.exit(0)` is run.
+ try {
+ // We have to use two commands because `Runtime.exec()` does not like `*`.
+ privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/Cookies");
+ privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/Cookies-journal");
+ } catch (IOException e) {
+ // Do nothing if an error is thrown.
+ }
}
// Clear DOM storage.
- WebStorage domStorage = WebStorage.getInstance();
- domStorage.deleteAllData();
+ if (clearEverything || sharedPreferences.getBoolean("clear_dom_storage", true)) {
+ // Ask `WebStorage` to clear the DOM storage.
+ WebStorage webStorage = WebStorage.getInstance();
+ webStorage.deleteAllData();
+
+ // Manually delete the DOM storage directory, as `WebStorage` sometimes will not flush its changes to disk before `System.exit(0)` is run.
+ try {
+ // We have to use a `String[]` because the directory contains a space and `Runtime.exec` will not escape the string correctly otherwise.
+ privacyBrowserRuntime.exec(new String[] {"rm", "-rf", privateDataDirectoryString + "/app_webview/Local Storage/"});
+ } catch (IOException e) {
+ // Do nothing if an error is thrown.
+ }
+ }
// Clear form data.
- WebViewDatabase webViewDatabase = WebViewDatabase.getInstance(this);
- webViewDatabase.clearFormData();
+ if (clearEverything || sharedPreferences.getBoolean("clear_form_data", true)) {
+ WebViewDatabase webViewDatabase = WebViewDatabase.getInstance(this);
+ webViewDatabase.clearFormData();
+
+ // Manually delete the form data database, as `WebViewDatabase` sometimes will not flush its changes to disk before `System.exit(0)` is run.
+ try {
+ // We have to use a `String[]` because the database contains a space and `Runtime.exec` will not escape the string correctly otherwise.
+ privacyBrowserRuntime.exec(new String[] {"rm", "-f", privateDataDirectoryString + "/app_webview/Web Data"});
+ privacyBrowserRuntime.exec(new String[] {"rm", "-f", privateDataDirectoryString + "/app_webview/Web Data-journal"});
+ } catch (IOException e) {
+ // Do nothing if an error is thrown.
+ }
+ }
- // Clear the cache. `true` includes disk files.
- mainWebView.clearCache(true);
+ // Clear the cache.
+ if (clearEverything || sharedPreferences.getBoolean("clear_cache", true)) {
+ // `true` includes disk files.
+ mainWebView.clearCache(true);
- // Clear the back/forward history.
- mainWebView.clearHistory();
+ // Manually delete the cache directories.
+ try {
+ // Delete the main cache directory.
+ privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/cache");
+
+ // Delete the secondary `Service Worker` cache directory. We have to use a `String[]` because the directory contains a space and `Runtime.exec` will not escape the string correctly otherwise.
+ privacyBrowserRuntime.exec(new String[] {"rm", "-rf", privateDataDirectoryString + "/app_webview/Service Worker/"});
+ } catch (IOException e) {
+ // Do nothing if an error is thrown.
+ }
+ }
- // Clear any SSL certificate preferences.
+ // Clear SSL certificate preferences.
mainWebView.clearSslPreferences();
+ // Clear the back/forward history.
+ mainWebView.clearHistory();
+
// Clear `formattedUrlString`.
formattedUrlString = null;
// Destroy the internal state of `mainWebView`.
mainWebView.destroy();
- // Manually delete cache folders.
- try {
- // Delete the main `cache` folder.
- privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/cache");
-
- // Delete the `app_webview` folder, which contains an additional `WebView` cache. See `https://code.google.com/p/android/issues/detail?id=233826&thanks=233826&ts=1486670530`.
- privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview");
- } catch (IOException e) {
- // Do nothing if an error is thrown.
+ // Manually delete the `app_webview` folder, which contains the cookies, DOM storage, form data, and `Service Worker` cache.
+ // See `https://code.google.com/p/android/issues/detail?id=233826&thanks=233826&ts=1486670530`.
+ if (clearEverything) {
+ try {
+ privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview");
+ } catch (IOException e) {
+ // Do nothing if an error is thrown.
+ }
}
// Close Privacy Browser. `finishAndRemoveTask` also removes Privacy Browser from the recent app list.
// Remove the terminated program from RAM. The status code is `0`.
System.exit(0);
break;
-
- default:
- break;
}
// Close the navigation drawer.
}
private void applyAppSettings() {
- // Get the shared preference values. `this` references the current context.
+ // Get a handle for `sharedPreferences`. `this` references the current context.
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
// Store the values from `sharedPreferences` in variables.
- String homepageString = sharedPreferences.getString("homepage", "https://duckduckgo.com");
+ String homepageString = sharedPreferences.getString("homepage", "https://start.duckduckgo.com");
String torHomepageString = sharedPreferences.getString("tor_homepage", "https://3g2upl4pq6kufc4m.onion");
String torSearchString = sharedPreferences.getString("tor_search", "https://3g2upl4pq6kufc4m.onion/html/?q=");
String torSearchCustomURLString = sharedPreferences.getString("tor_search_custom_url", "");