X-Git-Url: https://gitweb.stoutner.com/?a=blobdiff_plain;f=app%2Fsrc%2Fmain%2Fjava%2Fcom%2Fstoutner%2Fprivacybrowser%2Factivities%2FMainWebViewActivity.java;h=9cb3a71a4ce61628206eef57e3b0e81a1b438adf;hb=65753c63057ebb382a4ff2b14c1cc27460dc15e6;hp=c6f04a0fab47119a7ca812ea8b6af1c2a257e63c;hpb=9f551f25b53a30cca7b19b6e6bfc2d2520d9aa1b;p=PrivacyBrowserAndroid.git diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java index c6f04a0f..9cb3a71a 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java @@ -1,5 +1,5 @@ /* - * Copyright © 2015-2017 Soren Stoutner . + * Copyright © 2015-2018 Soren Stoutner . * * Download cookie code contributed 2017 Hendrik Knackstedt. Copyright assigned to Soren Stoutner . * @@ -54,6 +54,10 @@ import android.support.design.widget.NavigationView; 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; @@ -66,6 +70,7 @@ import android.text.Editable; 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; @@ -101,6 +106,7 @@ import android.widget.TextView; import com.stoutner.privacybrowser.BannerAd; import com.stoutner.privacybrowser.BuildConfig; import com.stoutner.privacybrowser.R; +import com.stoutner.privacybrowser.dialogs.AddDomainDialog; import com.stoutner.privacybrowser.dialogs.CreateBookmarkDialog; import com.stoutner.privacybrowser.dialogs.CreateBookmarkFolderDialog; import com.stoutner.privacybrowser.dialogs.CreateHomeScreenShortcutDialog; @@ -135,7 +141,7 @@ import java.util.Map; import java.util.Set; // 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 CreateBookmarkDialog.CreateBookmarkListener, CreateBookmarkFolderDialog.CreateBookmarkFolderListener, CreateHomeScreenShortcutDialog.CreateHomeScreenSchortcutListener, +public class MainWebViewActivity extends AppCompatActivity implements AddDomainDialog.AddDomainListener, CreateBookmarkDialog.CreateBookmarkListener, CreateBookmarkFolderDialog.CreateBookmarkFolderListener, CreateHomeScreenShortcutDialog.CreateHomeScreenSchortcutListener, DownloadFileDialog.DownloadFileListener, DownloadImageDialog.DownloadImageListener, EditBookmarkDialog.EditBookmarkListener, EditBookmarkFolderDialog.EditBookmarkFolderListener, HttpAuthenticationDialog.HttpAuthenticationListener, NavigationView.OnNavigationItemSelectedListener, PinnedSslCertificateMismatchDialog.PinnedSslCertificateMismatchListener, SslCertificateErrorDialog.SslCertificateErrorListener, UrlHistoryDialog.UrlHistoryListener { @@ -162,6 +168,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `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; @@ -174,12 +183,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `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; - // The pinned domain SSL Certificate variables are public static so they can be accessed from `PinnedSslCertificateMismatchDialog`. They are also used in `onCreate()` and `applyDomainSettings()`. + // `domainSettingsDatabaseId` is public static so it can be accessed from `PinnedSslCertificateMismatchDialog`. It is also used in `onCreate()`, `onOptionsItemSelected()`, and `applyDomainSettings()`. public static int domainSettingsDatabaseId; + + // The pinned domain SSL Certificate variables are public static so they can be accessed from `PinnedSslCertificateMismatchDialog`. They are also used in `onCreate()` and `applyDomainSettings()`. public static String pinnedDomainSslIssuedToCNameString; public static String pinnedDomainSslIssuedToONameString; public static String pinnedDomainSslIssuedToUNameString; @@ -275,7 +289,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `translucentNavigationBarOnFullscreen` is used in `onCreate()` and `applyAppSettings()`. private boolean translucentNavigationBarOnFullscreen; - // `currentDomainName` is used in `onCreate()`, `onNavigationItemSelected()`, `onSslMismatchProceed()`, and `applyDomainSettings()`. + // `reapplyDomainSettingsOnRestart` is used in `onCreate()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onRestart()`, and `onAddDomain()`, . + private boolean reapplyDomainSettingsOnRestart; + + // `currentDomainName` is used in `onCreate()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onAddDomain()`, and `applyDomainSettings()`. private String currentDomainName; // `ignorePinnedSslCertificateForDomain` is used in `onCreate()`, `onSslMismatchProceed()`, and `applyDomainSettings()`. @@ -284,7 +301,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `waitingForOrbot` is used in `onCreate()` and `applyAppSettings()`. private boolean waitingForOrbot; - // `domainSettingsApplied` is used in `applyDomainSettings()` and `setDisplayWebpageImages()`. + // `domainSettingsApplied` is used in `prepareOptionsMenu()`, `applyDomainSettings()`, and `setDisplayWebpageImages()`. private boolean domainSettingsApplied; // `displayWebpageImagesInt` is used in `applyDomainSettings()` and `setDisplayWebpageImages()`. @@ -317,13 +334,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `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()`. @@ -387,6 +400,52 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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 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); @@ -405,16 +464,16 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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); @@ -426,8 +485,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } }); - // 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. @@ -784,36 +843,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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 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. @SuppressWarnings("deprecation") @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { - if (url.startsWith("mailto:")) { // Load the URL in an external email program because it begins with `mailto:`. - // We use `ACTION_SENDTO` instead of `ACTION_SEND` so that only email programs are launched. + if (url.startsWith("mailto:")) { // Load the email address in an external email program. + // Use `ACTION_SENDTO` instead of `ACTION_SEND` so that only email programs are launched. Intent emailIntent = new Intent(Intent.ACTION_SENDTO); // Parse the url and set it as the data for the `Intent`. @@ -825,6 +862,21 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Make it so. startActivity(emailIntent); + // Returning `true` indicates the application is handling the URL. + return true; + } else if (url.startsWith("tel:")) { // Load the phone number in the dialer. + // `ACTION_DIAL` open the dialer and loads the phone number, but waits for the user to place the call. + Intent dialIntent = new Intent(Intent.ACTION_DIAL); + + // Add the phone number to the intent. + dialIntent.setData(Uri.parse(url)); + + // `FLAG_ACTIVITY_NEW_TASK` opens the dialer in a new task instead as part of Privacy Browser. + dialIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + // Make it so. + startActivity(dialIntent); + // Returning `true` indicates the application is handling the URL. return true; } else { // Load the URL in Privacy Browser. @@ -1102,7 +1154,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook }); } + // 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); @@ -1196,7 +1251,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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)); @@ -1304,11 +1359,21 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook @Override public void onRestart() { + // Run the default commands. super.onRestart(); // Apply the app settings, which may have been changed in `SettingsActivity`. applyAppSettings(); + // Apply the domain settings if returning from the Domains Activity. + if (reapplyDomainSettingsOnRestart) { + // Reset `reapplyDomainSettingsOnRestart`. + reapplyDomainSettingsOnRestart = false; + + // Reapply the domain settings. + applyDomainSettings(formattedUrlString); + } + // Update the privacy icon. `true` runs `invalidateOptionsMenu` as the last step. updatePrivacyIcons(true); @@ -1349,6 +1414,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `onResume()` runs after `onStart()`, which runs after `onCreate()` and `onRestart()`. @Override public void onResume() { + // Run the default commands. super.onResume(); // Resume JavaScript (if enabled). @@ -1419,10 +1485,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook @Override public boolean onPrepareOptionsMenu(Menu menu) { // Get handles for the menu items. + MenuItem addOrEditDomain = menu.findItem(R.id.add_or_edit_domain); MenuItem toggleFirstPartyCookiesMenuItem = menu.findItem(R.id.toggle_first_party_cookies); 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); @@ -1430,6 +1498,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook MenuItem displayImagesMenuItem = menu.findItem(R.id.display_images); MenuItem refreshMenuItem = menu.findItem(R.id.refresh); + // Set the text for the domain menu item. + if (domainSettingsApplied) { + addOrEditDomain.setTitle(R.string.edit_domain_settings); + } else { + addOrEditDomain.setTitle(R.string.add_domain_settings); + } + // Set the status of the menu item checkboxes. toggleFirstPartyCookiesMenuItem.setChecked(firstPartyCookiesEnabled); toggleThirdPartyCookiesMenuItem.setChecked(thirdPartyCookiesEnabled); @@ -1467,6 +1542,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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; @@ -1530,7 +1608,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Run all the other default commands. super.onPrepareOptionsMenu(menu); - // `return true` displays the menu. + // Display the menu. return true; } @@ -1540,10 +1618,32 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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. switch (menuItemId) { + case R.id.add_or_edit_domain: + if (domainSettingsApplied) { // Edit the current domain settings. + // Reapply the domain settings on returning to `MainWebViewActivity`. + reapplyDomainSettingsOnRestart = true; + currentDomainName = ""; + + // Create an intent to launch the domains activity. + Intent domainsIntent = new Intent(this, DomainsActivity.class); + + // Put extra information instructing the domains activity to directly load the current domain. + domainsIntent.putExtra("LoadDomain", domainSettingsDatabaseId); + + // Make it so. + startActivity(domainsIntent); + } else { // Add a new domain. + // Show the add domain `AlertDialog`. + AppCompatDialogFragment addDomainDialog = new AddDomainDialog(); + addDomainDialog.show(getSupportFragmentManager(), getResources().getString(R.string.add_domain)); + } + return true; + case R.id.toggle_javascript: // Switch the status of javaScriptEnabled. javaScriptEnabled = !javaScriptEnabled; @@ -1713,9 +1813,16 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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. } @@ -1839,6 +1946,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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(); @@ -1908,7 +2021,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook break; case R.id.domains: - // Reset `currentDomainName` so that domain settings are reapplied after returning to `MainWebViewActivity`. + // Reapply the domain settings on returning to `MainWebViewActivity`. + reapplyDomainSettingsOnRestart = true; currentDomainName = ""; // Launch `DomainsActivity`. @@ -1917,7 +2031,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook break; case R.id.settings: - // Reset `currentDomainName` so that domain settings are reapplied after returning to `MainWebViewActivity`. + // Reapply the domain settings on returning to `MainWebViewActivity`. + reapplyDomainSettingsOnRestart = true; currentDomainName = ""; // Launch `SettingsActivity`. @@ -1970,10 +2085,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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"); @@ -2181,7 +2296,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook }); // 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)); @@ -2218,7 +2333,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook }); // 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)); @@ -2241,6 +2356,33 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } } + @Override + public void onAddDomain(AppCompatDialogFragment dialogFragment) { + // Reapply the domain settings on returning to `MainWebViewActivity`. + reapplyDomainSettingsOnRestart = true; + currentDomainName = ""; + + // Get the new domain name `String` from `dialogFragment`. + EditText domainNameEditText = dialogFragment.getDialog().findViewById(R.id.domain_name_edittext); + String domainNameString = domainNameEditText.getText().toString(); + + // Initialize the database handler. `this` specifies the context. The two `nulls` do not specify the database name or a `CursorFactory`. + // The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`. + DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(this, null, null, 0); + + // Create the domain and store the database ID in `currentDomainDatabaseId`. + int newDomainDatabaseId = domainsDatabaseHelper.addDomain(domainNameString); + + // Create an intent to launch the domains activity. + Intent domainsIntent = new Intent(this, DomainsActivity.class); + + // Put extra information instructing the domains activity to directly load the current domain. + domainsIntent.putExtra("LoadDomain", newDomainDatabaseId); + + // Make it so. + startActivity(domainsIntent); + } + @Override public void onCreateBookmark(AppCompatDialogFragment dialogFragment) { // Get the `EditTexts` from the `dialogFragment`. @@ -2319,21 +2461,28 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook @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); + + // Setup the shortcut intent. + Intent shortcutIntent = new Intent(); + shortcutIntent.setAction(Intent.ACTION_VIEW); + shortcutIntent.setData(Uri.parse(formattedUrlString)); - // 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); + // 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 @@ -2712,10 +2861,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } 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="); @@ -3067,6 +3216,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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. @@ -3129,6 +3281,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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. @@ -3235,9 +3390,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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); }