/*
- * Copyright © 2015-2017 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2015-2018 Soren Stoutner <soren@stoutner.com>.
*
* Download cookie code contributed 2017 Hendrik Knackstedt. Copyright assigned to Soren Stoutner <soren@stoutner.com>.
*
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
+// `ShortcutInfoCompat`, `ShortcutManagerCompat`, and `IconCompat` can be switched to the non-compat version once API >= 26.
+import android.support.v4.content.pm.ShortcutInfoCompat;
+import android.support.v4.content.pm.ShortcutManagerCompat;
+import android.support.v4.graphics.drawable.IconCompat;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v4.widget.SwipeRefreshLayout;
import android.text.Spanned;
import android.text.TextWatcher;
import android.text.style.ForegroundColorSpan;
+import android.util.Log;
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;
+ // `appliedUserAgentString` is public static so it can be accessed from `ViewSourceActivity`. It is also used in `applyDomainSettings()`.
+ public static String appliedUserAgentString;
+
// `displayWebpageImagesBoolean` is public static so it can be accessed from `DomainSettingsFragment`. It is also used in `applyAppSettings()` and `applyDomainSettings()`.
public static boolean displayWebpageImagesBoolean;
// `restartFromBookmarksActivity` is public static so it can be accessed from `BookmarksActivity`. It is also used in `onRestart()`.
public static boolean restartFromBookmarksActivity;
+ // `easyListVersion` is public static so it can be accessed from `AboutTabFragment`. It is also used in `onCreate()`.
+ public static String easyListVersion;
+
// `currentBookmarksFolder` is public static so it can be accessed from `BookmarksActivity`. It is also used in `onCreate()`, `onBackPressed()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and
// `loadBookmarksFolder()`.
public static String currentBookmarksFolder;
// `urlTextBox` is used in `onCreate()`, `onOptionsItemSelected()`, `loadUrlFromTextBox()`, `loadUrl()`, and `highlightUrlText()`.
private EditText urlTextBox;
- // `redColorSpan` is used in `onCreate()` and `highlightUrlText()`.
+ // The color spans are used in `onCreate()` and `highlightUrlText()`.
private ForegroundColorSpan redColorSpan;
-
- // `initialGrayColorSpan` is sued in `onCreate()` and `highlightUrlText()`.
private ForegroundColorSpan initialGrayColorSpan;
-
- // `finalGrayColorSpam` is used in `onCreate()` and `highlightUrlText()`.
private ForegroundColorSpan finalGrayColorSpan;
// `adView` is used in `onCreate()` and `onConfigurationChanged()`.
// Run the default commands.
super.onCreate(savedInstanceState);
+ // **DEBUG** Log the beginning of the loading of the ad blocker.
+ Log.i("AdBlocker", "Begin loading ad blocker");
+
+ // Initialize `adServerSet`.
+ final Set<String> adServersSet = new HashSet<>();
+
+ // Load the list of ad servers into memory.
+ try {
+ // Load `easylist.txt` into a `BufferedReader`.
+ BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(getAssets().open("easylist.txt")));
+
+ // Create a string for storing each ad server.
+ String adBlockerEntry;
+
+ // Populate `adServersSet`.
+ while ((adBlockerEntry = bufferedReader.readLine()) != null) {
+ //noinspection StatementWithEmptyBody
+ if (adBlockerEntry.contains("##") || adBlockerEntry.contains("#?#") || adBlockerEntry.contains("#@#") || adBlockerEntry.startsWith("[")) {
+ // Entries that contain `##`, `#?#`, and `#@#` are for hiding elements in the main page's HTML. Entries that start with `[` describe the AdBlock compatibility level.
+
+ // Do nothing. Privacy Browser does not currently use these entries.
+
+ // **DEBUG** Log the entries that are not added.
+ // Log.i("AdBlocker", "Not added: " + adBlockerEntry);
+ } else if (adBlockerEntry.startsWith("!")){ // Entries that begin with `!` are comments.
+ if (adBlockerEntry.startsWith("! Version:")) {
+ // Store the EasyList version number.
+ easyListVersion = adBlockerEntry.substring(11);
+ }
+
+ // **DEBUG** Log the entries that are not added.
+ // Log.i("AdBlocker", "Not added: " + adBlockerEntry);
+ } else {
+ adServersSet.add(adBlockerEntry);
+ }
+ }
+
+ // Close `bufferedReader`.
+ bufferedReader.close();
+ } catch (IOException e) {
+ // The asset exists, so the `IOException` will never be thrown.
+ }
+
+ // **DEBUG** Log the finishing of the loading of the ad blocker.
+ Log.i("AdBlocker", "Finish loading ad blocker");
+
// Set the content view.
setContentView(R.layout.main_drawerlayout);
appBar.setCustomView(R.layout.url_app_bar);
appBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
- // Initialize the `ForegroundColorSpans` and `StyleSpan` for highlighting `urlTextBox`. We have to use the deprecated `getColor()` until API >= 23.
+ // Initialize the foreground color spans for highlighting the URLs. We have to use the deprecated `getColor()` until API >= 23.
redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700));
initialGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500));
finalGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500));
// Get a handle for `urlTextBox`.
- urlTextBox = appBar.getCustomView().findViewById(R.id.url_edittext);
+ urlTextBox = findViewById(R.id.url_edittext);
// Remove the formatting from `urlTextBar` when the user is editing the text.
- urlTextBox.setOnFocusChangeListener((v, hasFocus) -> {
+ urlTextBox.setOnFocusChangeListener((View v, boolean hasFocus) -> {
if (hasFocus) { // The user is editing `urlTextBox`.
// Remove the highlighting.
urlTextBox.getText().removeSpan(redColorSpan);
}
});
- // Set the `Go` button on the keyboard to load the URL in `urlTextBox`.
- urlTextBox.setOnKeyListener((v, keyCode, event) -> {
+ // Set the go button on the keyboard to load the URL in `urlTextBox`.
+ urlTextBox.setOnKeyListener((View v, int keyCode, KeyEvent event) -> {
// If the event is a key-down event on the `enter` button, load the URL.
if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
// Load the URL into the mainWebView and consume the event.
// drawerToggle creates the hamburger icon at the start of the AppBar.
drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, supportAppBar, R.string.open_navigation_drawer, R.string.close_navigation_drawer);
- // Initialize `adServerSet`.
- final Set<String> adServersSet = new HashSet<>();
-
- // Load the list of ad servers into memory.
- try {
- // Load `pgl.yoyo.org_adservers.txt` into a `BufferedReader`.
- BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(getAssets().open("pgl.yoyo.org_adservers.txt")));
-
- // Create a string for storing each ad server.
- String adServer;
-
- // Populate `adServersSet`.
- while ((adServer = bufferedReader.readLine()) != null) {
- adServersSet.add(adServer);
- }
-
- // Close `bufferedReader`.
- bufferedReader.close();
- } catch (IOException ioException) {
- // We're pretty sure the asset exists, so we don't need to worry about the `IOException` ever being thrown.
- }
-
mainWebView.setWebViewClient(new WebViewClient() {
// `shouldOverrideUrlLoading` makes this `WebView` the default handler for URLs inside the app, so that links are not kicked out to other apps.
// We have to use the deprecated `shouldOverrideUrlLoading` until API >= 24.
});
}
+ // Update the progress bar.
progressBar.setProgress(progress);
+
+ // Set the visibility of the progress bar.
if (progress < 100) {
// Show the progress bar.
progressBar.setVisibility(View.VISIBLE);
registerForContextMenu(mainWebView);
// Allow the downloading of files.
- mainWebView.setDownloadListener((url, userAgent, contentDisposition, mimetype, contentLength) -> {
+ mainWebView.setDownloadListener((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(), getString(R.string.download));
MenuItem toggleThirdPartyCookiesMenuItem = menu.findItem(R.id.toggle_third_party_cookies);
MenuItem toggleDomStorageMenuItem = menu.findItem(R.id.toggle_dom_storage);
MenuItem toggleSaveFormDataMenuItem = menu.findItem(R.id.toggle_save_form_data);
+ MenuItem clearDataMenuItem = menu.findItem(R.id.clear_data);
MenuItem clearCookiesMenuItem = menu.findItem(R.id.clear_cookies);
MenuItem clearDOMStorageMenuItem = menu.findItem(R.id.clear_dom_storage);
MenuItem clearFormDataMenuItem = menu.findItem(R.id.clear_form_data);
WebViewDatabase mainWebViewDatabase = WebViewDatabase.getInstance(this);
clearFormDataMenuItem.setEnabled(mainWebViewDatabase.hasFormData());
+ // Enable `Clear Data` if any of the submenu items are enabled.
+ clearDataMenuItem.setEnabled(clearCookiesMenuItem.isEnabled() || clearDOMStorageMenuItem.isEnabled() || clearFormDataMenuItem.isEnabled());
+
// Initialize font size variables.
int fontSize = mainWebView.getSettings().getTextZoom();
String fontSizeTitle;
// Run all the other default commands.
super.onPrepareOptionsMenu(menu);
- // `return true` displays the menu.
+ // Display the menu.
return true;
}
// removeAllCookies is deprecated, but it is required for API < 21.
@SuppressWarnings("deprecation")
public boolean onOptionsItemSelected(MenuItem menuItem) {
+ // Get the selected menu item ID.
int menuItemId = menuItem.getItemId();
// Set the commands that relate to the menu entries.
WebStorage webStorage = WebStorage.getInstance();
webStorage.deleteAllData();
- // Manually remove `IndexedDB` if it exists.
+ // Manually delete the DOM storage files and directories.
try {
+ // A `String[]` must be used because the directory contains a space and `Runtime.exec` will otherwise not escape the string correctly.
+ privacyBrowserRuntime.exec(new String[] {"rm", "-rf", privateDataDirectoryString + "/app_webview/Local Storage/"});
+
+ // Multiple commands must be used because `Runtime.exec()` does not like `*`.
privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/IndexedDB");
+ privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager");
+ privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager-journal");
+ privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/databases");
} catch (IOException e) {
// Do nothing if an error is thrown.
}
printManager.print(getString(R.string.privacy_browser_web_page), printDocumentAdapter, null);
return true;
+ case R.id.view_source:
+ // Launch the Vew Source activity.
+ Intent viewSourceIntent = new Intent(this, ViewSourceActivity.class);
+ startActivity(viewSourceIntent);
+ return true;
+
case R.id.add_to_homescreen:
// Show the `CreateHomeScreenShortcutDialog` `AlertDialog` and name this instance `R.string.create_shortcut`.
AppCompatDialogFragment createHomeScreenShortcutDialogFragment = new CreateHomeScreenShortcutDialog();
// Manually delete the DOM storage files and directories, 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.
+ // A `String[]` must be used because the directory contains a space and `Runtime.exec` will otherwise not escape the string correctly.
privacyBrowserRuntime.exec(new String[] {"rm", "-rf", privateDataDirectoryString + "/app_webview/Local Storage/"});
- // We have to use multiple commands because `Runtime.exec()` does not like `*`.
+ // Multiple commands must be used because `Runtime.exec()` does not like `*`.
privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/IndexedDB");
privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager");
privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager-journal");
});
// Add a `Download Image` entry.
- menu.add(R.string.download_image).setOnMenuItemClickListener(item -> {
+ menu.add(R.string.download_image).setOnMenuItemClickListener((MenuItem item) -> {
// Show the `DownloadImageDialog` `AlertDialog` and name this instance `@string/download`.
AppCompatDialogFragment downloadImageDialogFragment = DownloadImageDialog.imageUrl(imageUrl);
downloadImageDialogFragment.show(getSupportFragmentManager(), getString(R.string.download));
});
// Add a `Download Image` entry.
- menu.add(R.string.download_image).setOnMenuItemClickListener(item -> {
+ menu.add(R.string.download_image).setOnMenuItemClickListener((MenuItem item) -> {
// Show the `DownloadImageDialog` `AlertDialog` and name this instance `@string/download`.
AppCompatDialogFragment downloadImageDialogFragment = DownloadImageDialog.imageUrl(imageUrl);
downloadImageDialogFragment.show(getSupportFragmentManager(), getString(R.string.download));
@Override
public void onCreateHomeScreenShortcut(AppCompatDialogFragment dialogFragment) {
- // Get shortcutNameEditText from the alert dialog.
+ // Get the shortcut name.
EditText shortcutNameEditText = dialogFragment.getDialog().findViewById(R.id.shortcut_name_edittext);
+ String shortcutNameString = shortcutNameEditText.getText().toString();
+
+ // Convert the favorite icon bitmap to an `Icon`. `IconCompat` is required until API >= 26.
+ IconCompat favoriteIcon = IconCompat.createWithBitmap(favoriteIconBitmap);
- // Create the bookmark shortcut based on formattedUrlString.
- Intent bookmarkShortcut = new Intent();
- bookmarkShortcut.setAction(Intent.ACTION_VIEW);
- bookmarkShortcut.setData(Uri.parse(formattedUrlString));
-
- // Place the bookmark shortcut on the home screen.
- Intent placeBookmarkShortcut = new Intent();
- placeBookmarkShortcut.putExtra("android.intent.extra.shortcut.INTENT", bookmarkShortcut);
- placeBookmarkShortcut.putExtra("android.intent.extra.shortcut.NAME", shortcutNameEditText.getText().toString());
- placeBookmarkShortcut.putExtra("android.intent.extra.shortcut.ICON", favoriteIconBitmap);
- placeBookmarkShortcut.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
- sendBroadcast(placeBookmarkShortcut);
+ // Setup the shortcut intent.
+ Intent shortcutIntent = new Intent();
+ shortcutIntent.setAction(Intent.ACTION_VIEW);
+ shortcutIntent.setData(Uri.parse(formattedUrlString));
+
+ // Create a shortcut info builder. The shortcut name becomes the shortcut ID.
+ ShortcutInfoCompat.Builder shortcutInfoBuilder = new ShortcutInfoCompat.Builder(this, shortcutNameString);
+
+ // Add the required fields to the shortcut info builder.
+ shortcutInfoBuilder.setIcon(favoriteIcon);
+ shortcutInfoBuilder.setIntent(shortcutIntent);
+ shortcutInfoBuilder.setShortLabel(shortcutNameString);
+
+ // Request the pin. `ShortcutManagerCompat` can be switched to `ShortcutManager` once API >= 26.
+ ShortcutManagerCompat.requestPinShortcut(this, shortcutInfoBuilder.build(), null);
}
@Override
}
private void applyAppSettings() {
- // Get a handle for `sharedPreferences`. `this` references the current context.
+ // Get a handle for the shared preferences.
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
- // Store the values from `sharedPreferences` in variables.
+ // Store the values from the shared preferences in variables.
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=");
// Use the selected user agent.
mainWebView.getSettings().setUserAgentString(userAgentString);
}
+
+ // Store the applied user agent string.
+ appliedUserAgentString = mainWebView.getSettings().getUserAgentString();
}
// Set a green background on `urlTextBox` to indicate that custom domain settings are being used. We have to use the deprecated `.getDrawable()` until the minimum API >= 21.
// Use the selected user agent.
mainWebView.getSettings().setUserAgentString(defaultUserAgentString);
}
+
+ // Store the applied user agent string.
+ appliedUserAgentString = mainWebView.getSettings().getUserAgentString();
}
// Set a transparent background on `urlTextBox`. We have to use the deprecated `.getDrawable()` until the minimum API >= 21.
private void highlightUrlText() {
String urlString = urlTextBox.getText().toString();
- if (urlString.startsWith("http://")) { // Highlight connections that are not encrypted.
+ if (urlString.startsWith("http://")) { // Highlight the protocol of connections that are not encrypted.
urlTextBox.getText().setSpan(redColorSpan, 0, 7, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
- } else if (urlString.startsWith("https://")) { // Highlight connections that are encrypted.
+ } else if (urlString.startsWith("https://")) { // De-emphasize the protocol of connections that are encrypted.
urlTextBox.getText().setSpan(initialGrayColorSpan, 0, 8, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
}