import com.stoutner.privacybrowser.helpers.AdHelper;
import com.stoutner.privacybrowser.helpers.BlocklistHelper;
import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper;
-import com.stoutner.privacybrowser.helpers.CheckPinnedMismatchHelper;
import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper;
import com.stoutner.privacybrowser.helpers.FileNameHelper;
import com.stoutner.privacybrowser.helpers.ProxyHelper;
// The WebView pager adapter is accessed from `HttpAuthenticationDialog`, `PinnedMismatchDialog`, and `SslCertificateErrorDialog`. It is also used in `onCreate()`, `onResume()`, and `addTab()`.
public static WebViewPagerAdapter webViewPagerAdapter;
- // The load URL on restart variables are public static so they can be accessed from `BookmarksActivity`. They are used in `onRestart()`.
- public static boolean loadUrlOnRestart;
- public static String urlToLoadOnRestart;
-
// `restartFromBookmarksActivity` is public static so it can be accessed from `BookmarksActivity`. It is also used in `onRestart()`.
public static boolean restartFromBookmarksActivity;
// Reapply the domain settings if the URL is not null, which can happen if an empty tab is active when returning from settings.
if (nestedScrollWebView.getUrl() != null) {
- applyDomainSettings(nestedScrollWebView, nestedScrollWebView.getUrl(), false, true);
+ applyDomainSettings(nestedScrollWebView, nestedScrollWebView.getUrl(), false, true, false);
}
}
}
}
- // Load the URL on restart (used when loading a bookmark).
- if (loadUrlOnRestart) {
- // Load the specified URL.
- loadUrl(currentWebView, urlToLoadOnRestart);
-
- // Reset the load on restart tracker.
- loadUrlOnRestart = false;
- }
-
// Update the bookmarks drawer if returning from the Bookmarks activity.
if (restartFromBookmarksActivity) {
// Close the bookmarks drawer.
String previousUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() - 1).getUrl();
// Apply the domain settings.
- applyDomainSettings(currentWebView, previousUrl, false, false);
+ applyDomainSettings(currentWebView, previousUrl, false, false, false);
// Load the previous website in the history.
currentWebView.goBack();
String nextUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() + 1).getUrl();
// Apply the domain settings.
- applyDomainSettings(currentWebView, nextUrl, false, false);
+ applyDomainSettings(currentWebView, nextUrl, false, false, false);
// Load the next website in the history.
currentWebView.goForward();
@Override
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
- // Store the hit test result.
+ // Get the hit test result.
final WebView.HitTestResult hitTestResult = currentWebView.getHitTestResult();
// Define the URL strings.
// Get the image URL.
imageUrl = hitTestResult.getExtra();
- // Set the image URL as the title of the context menu.
- menu.setHeaderTitle(imageUrl);
+ // Remove the incorrect lint warning below that the image URL might be null.
+ assert imageUrl != null;
+
+ // Set the context menu title.
+ if (imageUrl.startsWith("data:")) { // The image data is contained in within the URL, making it exceedingly long.
+ // Truncate the image URL before making it the title.
+ menu.setHeaderTitle(imageUrl.substring(0, 100));
+ } else { // The image URL does not contain the full image data.
+ // Set the image URL as the title of the context menu.
+ menu.setHeaderTitle(imageUrl);
+ }
// Add an Open in New Tab entry.
menu.add(R.string.open_image_in_new_tab).setOnMenuItemClickListener((MenuItem item) -> {
String previousUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() - 1).getUrl();
// Apply the domain settings.
- applyDomainSettings(currentWebView, previousUrl, false, false);
+ applyDomainSettings(currentWebView, previousUrl, false, false, false);
// Go back.
currentWebView.goBack();
// Sanitize the URL.
url = sanitizeUrl(url);
- // Apply the domain settings.
- applyDomainSettings(nestedScrollWebView, url, true, false);
-
- // Load the URL.
- nestedScrollWebView.loadUrl(url, customHeaders);
+ // Apply the domain settings and load the URL.
+ applyDomainSettings(nestedScrollWebView, url, true, false, true);
}
public void findPreviousOnPage(View view) {
// Get the file path string.
openFilePath = fileNameEditText.getText().toString();
+ // Apply the domain settings. This resets the favorite icon and removes any domain settings.
+ applyDomainSettings(currentWebView, "file://" + openFilePath, true, false, false);
+
// Check to see if the storage permission is needed.
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { // The storage permission has been granted.
// Open the file.
}
@Override
- public void onSaveWebpage(int saveType, DialogFragment dialogFragment) {
+ public void onSaveWebpage(int saveType, String originalUrlString, DialogFragment dialogFragment) {
// Get the dialog.
Dialog dialog = dialogFragment.getDialog();
EditText urlEditText = dialog.findViewById(R.id.url_edittext);
EditText fileNameEditText = dialog.findViewById(R.id.file_name_edittext);
- // Get the strings from the edit texts.
- saveWebpageUrl = urlEditText.getText().toString();
+ // Store the URL.
+ if ((originalUrlString != null) && originalUrlString.startsWith("data:")) {
+ // Save the original URL.
+ saveWebpageUrl = originalUrlString;
+ } else {
+ // Get the URL from the edit text, which may have been modified.
+ saveWebpageUrl = urlEditText.getText().toString();
+ }
+
+ // Get the file path from the edit text.
saveWebpageFilePath = fileNameEditText.getText().toString();
// Check to see if the storage permission is needed.
case StoragePermissionDialog.SAVE_ARCHIVE:
// Save the webpage archive.
- saveWebpageArchive();
+ saveWebpageArchive(saveWebpageFilePath);
break;
case StoragePermissionDialog.SAVE_IMAGE:
new SaveWebpageImage(this, this, saveWebpageFilePath, currentWebView).execute();
break;
}
+
+ // Reset the strings.
+ saveWebpageUrl = "";
+ saveWebpageFilePath = "";
} else { // The storage permission has not been granted.
// Get the external private directory file.
File externalPrivateDirectoryFile = getExternalFilesDir(null);
case StoragePermissionDialog.SAVE_ARCHIVE:
// Save the webpage archive.
- saveWebpageArchive();
+ saveWebpageArchive(saveWebpageFilePath);
break;
case StoragePermissionDialog.SAVE_IMAGE:
new SaveWebpageImage(this, this, saveWebpageFilePath, currentWebView).execute();
break;
}
+
+ // Reset the strings.
+ saveWebpageUrl = "";
+ saveWebpageFilePath = "";
} else { // The file path is in a public directory.
// Check if the user has previously denied the storage permission.
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { // Show a dialog explaining the request first.
// Display an error snackbar.
Snackbar.make(currentWebView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show();
}
-
- // Reset the open file path.
- openFilePath = "";
break;
case StoragePermissionDialog.SAVE_URL:
// Display an error snackbar.
Snackbar.make(currentWebView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show();
}
-
- // Reset the save strings.
- saveWebpageUrl = "";
- saveWebpageFilePath = "";
break;
case StoragePermissionDialog.SAVE_ARCHIVE:
// Check to see if the storage permission was granted. If the dialog was canceled the grant results will be empty.
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { // The storage permission was granted.
// Save the webpage archive.
- saveWebpageArchive();
+ saveWebpageArchive(saveWebpageFilePath);
} else { // The storage permission was not granted.
// Display an error snackbar.
Snackbar.make(currentWebView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show();
}
-
- // Reset the save webpage file path.
- saveWebpageFilePath = "";
break;
case StoragePermissionDialog.SAVE_IMAGE:
// Display an error snackbar.
Snackbar.make(currentWebView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show();
}
-
- // Reset the save webpage file path.
- saveWebpageFilePath = "";
break;
}
+
+ // Reset the strings.
+ openFilePath = "";
+ saveWebpageUrl = "";
+ saveWebpageFilePath = "";
}
}
// Select the corresponding tab if it does not match the currently selected page. This will happen if the page was scrolled by creating a new tab.
if (tabLayout.getSelectedTabPosition() != position) {
- // Create a handler to select the tab.
- Handler selectTabHandler = new Handler();
-
- // Create a runnable to select the tab.
- Runnable selectTabRunnable = () -> {
+ // Wait until the new tab has been created.
+ tabLayout.post(() -> {
// Get a handle for the tab.
TabLayout.Tab tab = tabLayout.getTabAt(position);
// Select the tab.
tab.select();
- };
-
- // Select the tab layout after 150 milliseconds, which leaves enough time for a new tab to be inflated. TODO.
- selectTabHandler.postDelayed(selectTabRunnable, 150);
+ });
}
}
@Override
public void navigateHistory(String url, int steps) {
// Apply the domain settings.
- applyDomainSettings(currentWebView, url, false, false);
+ applyDomainSettings(currentWebView, url, false, false, false);
// Load the history entry.
currentWebView.goBackOrForward(steps);
String previousUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() - 1).getUrl();
// Apply the domain settings.
- applyDomainSettings(currentWebView, previousUrl, false, false);
+ applyDomainSettings(currentWebView, previousUrl, false, false, false);
// Go back.
currentWebView.goBack();
// `reloadWebsite` is used if returning from the Domains activity. Otherwise JavaScript might not function correctly if it is newly enabled.
@SuppressLint("SetJavaScriptEnabled")
- private void applyDomainSettings(NestedScrollWebView nestedScrollWebView, String url, boolean resetTab, boolean reloadWebsite) {
+ private void applyDomainSettings(NestedScrollWebView nestedScrollWebView, String url, boolean resetTab, boolean reloadWebsite, boolean loadUrl) {
// Store the current URL.
nestedScrollWebView.setCurrentUrl(url);
if (reloadWebsite) {
nestedScrollWebView.reload();
}
+
+ // Load the URL if directed. This makes sure that the domain settings are properly loaded before the URL. By using `loadUrl()`, instead of `loadUrlFromBase()`, the Referer header will never be sent.
+ if (loadUrl) {
+ nestedScrollWebView.loadUrl(url, customHeaders);
+ }
}
private void applyProxy(boolean reloadWebViews) {
}
private void closeCurrentTab() {
+ // Pause the current WebView.
+ currentWebView.onPause();
+
+ // Pause the current WebView JavaScript timers.
+ currentWebView.pauseTimers();
+
// Get the current tab number.
int currentTabNumber = tabLayout.getSelectedTabPosition();
appBarLayout.setExpanded(true);
}
- private void saveWebpageArchive() {
+ private void saveWebpageArchive(String filePath) {
// Save the webpage archive.
- currentWebView.saveWebArchive(saveWebpageFilePath);
+ currentWebView.saveWebArchive(filePath);
// Display a snackbar.
- Snackbar saveWebpageArchiveSnackbar = Snackbar.make(currentWebView, getString(R.string.file_saved) + " " + saveWebpageFilePath, Snackbar.LENGTH_SHORT);
+ Snackbar saveWebpageArchiveSnackbar = Snackbar.make(currentWebView, getString(R.string.file_saved) + " " + filePath, Snackbar.LENGTH_SHORT);
// Add an open option to the snackbar.
saveWebpageArchiveSnackbar.setAction(R.string.open, (View view) -> {
// Get a file for the file name string.
- File file = new File(saveWebpageFilePath);
+ File file = new File(filePath);
// Declare a file URI variable.
Uri fileUri;
// Show the snackbar.
saveWebpageArchiveSnackbar.show();
-
- // Reset the save Webpage file path.
- saveWebpageFilePath = "";
}
private void clearAndExit() {
// Handle the URL according to the type.
if (url.startsWith("http")) { // Load the URL in Privacy Browser.
- // Apply the domain settings for the new URL. This doesn't do anything if the domain has not changed.
- applyDomainSettings(nestedScrollWebView, url, true, false);
-
// Load the URL. By using `loadUrl()`, instead of `loadUrlFromBase()`, the Referer header will never be sent.
- nestedScrollWebView.loadUrl(url, customHeaders);
+ loadUrl(nestedScrollWebView, url);
// Returning true indicates that Privacy Browser is manually handling the loading of the URL.
// Custom headers cannot be added if false is returned and the WebView handles the loading of the URL.
}
}
- // Clear the cache and history if Incognito Mode is enabled.
+ // Clear the cache, history, and logcat if Incognito Mode is enabled.
if (incognitoModeEnabled) {
// Clear the cache. `true` includes disk files.
nestedScrollWebView.clearCache(true);
// Delete the secondary `Service Worker` cache directory.
// A `String[]` must be used because the directory contains a space and `Runtime.exec` will not escape the string correctly otherwise.
Runtime.getRuntime().exec(new String[]{"rm", "-rf", privateDataDirectoryString + "/app_webview/Service Worker/"});
- } catch (IOException e) {
+ } catch (IOException exception) {
// Do nothing if an error is thrown.
}
+
+ // Clear the logcat.
+ try {
+ // Clear the logcat. `-c` clears the logcat. `-b all` clears all the buffers (instead of just crash, main, and system).
+ Runtime.getRuntime().exec("logcat -b all -c");
+ } catch (IOException exception) {
+ // Do nothing.
+ }
}
// Get the current page position.
int currentPagePosition = webViewPagerAdapter.getPositionForId(nestedScrollWebView.getWebViewFragmentId());
- // Check the current website information against any pinned domain information if the current IP addresses have been loaded.
- if ((nestedScrollWebView.hasPinnedSslCertificate() || nestedScrollWebView.hasPinnedIpAddresses()) && nestedScrollWebView.hasCurrentIpAddresses() &&
- !nestedScrollWebView.ignorePinnedDomainInformation()) {
- CheckPinnedMismatchHelper.checkPinnedMismatch(getSupportFragmentManager(), nestedScrollWebView);
- }
-
// Get the current URL from the nested scroll WebView. This is more accurate than using the URL passed into the method, which is sometimes not the final one.
String currentUrl = nestedScrollWebView.getUrl();
inputMethodManager.showSoftInput(urlEditText, 0);
// Apply the domain settings. This clears any settings from the previous domain.
- applyDomainSettings(nestedScrollWebView, "", true, false);
+ applyDomainSettings(nestedScrollWebView, "", true, false, false);
// Only populate the title text view if the tab has been fully created.
if (tab != null) {