X-Git-Url: https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=blobdiff_plain;f=app%2Fsrc%2Fmain%2Fjava%2Fcom%2Fstoutner%2Fprivacybrowser%2Factivities%2FMainWebViewActivity.java;h=4627ff4c157110582ccb02b072b6c28c5d3c169c;hp=5e4a21d9b6200a6508511889831b93868b9bec1c;hb=eb5895f27130aee9da36f06da293ae4a788c75c0;hpb=930adb36c4ef786be96f8d11a238c87ac987c82e 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 5e4a21d9..4627ff4c 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java @@ -292,7 +292,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook private CoordinatorLayout rootCoordinatorLayout; // `mainWebView` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onRestart()`, `onCreateContextMenu()`, `findPreviousOnPage()`, - // `findNextOnPage()`, `closeFindOnPage()`, `loadUrlFromTextBox()`, `onSslMismatchBack()`, `setDisplayWebpageImages()`, and `applyProxyThroughOrbot()`. + // `findNextOnPage()`, `closeFindOnPage()`, `loadUrlFromTextBox()`, `onSslMismatchBack()`, and `applyProxyThroughOrbot()`. private WebView mainWebView; // `fullScreenVideoFrameLayout` is used in `onCreate()` and `onConfigurationChanged()`. @@ -331,9 +331,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `nightMode` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyDomainSettings()`. private boolean nightMode; - // `displayWebpageImagesBoolean` is used in `applyAppSettings()` and `applyDomainSettings()`. - private boolean displayWebpageImagesBoolean; - // 'homepage' is used in `onCreate()`, `onNavigationItemSelected()`, and `applyProxyThroughOrbot()`. private String homepage; @@ -398,6 +395,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `displayingFullScreenVideo` is used in `onCreate()` and `onResume()`. private boolean displayingFullScreenVideo; + // `downloadWithExternalApp` is used in `onCreate()`, `onCreateContextMenu()`, and `applyDomainSettings()`. + private boolean downloadWithExternalApp; + // `currentDomainName` is used in `onCreate()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onAddDomain()`, and `applyDomainSettings()`. private String currentDomainName; @@ -410,18 +410,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `waitingForOrbot` is used in `onCreate()`, `onResume()`, and `applyProxyThroughOrbot()`. private boolean waitingForOrbot; - // `domainSettingsApplied` is used in `prepareOptionsMenu()`, `applyDomainSettings()`, and `setDisplayWebpageImages()`. + // `domainSettingsApplied` is used in `prepareOptionsMenu()` and `applyDomainSettings()`. private boolean domainSettingsApplied; // `domainSettingsJavaScriptEnabled` is used in `onOptionsItemSelected()` and `applyDomainSettings()`. private Boolean domainSettingsJavaScriptEnabled; - // `displayWebpageImagesInt` is used in `applyDomainSettings()` and `setDisplayWebpageImages()`. - private int displayWebpageImagesInt; - - // `onTheFlyDisplayImagesSet` is used in `applyDomainSettings()` and `setDisplayWebpageImages()`. - private boolean onTheFlyDisplayImagesSet; - // `waitingForOrbotHtmlString` is used in `onCreate()` and `applyProxyThroughOrbot()`. private String waitingForOrbotHtmlString; @@ -928,8 +922,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Hide the keyboard (if displayed). inputMethodManager.hideSoftInputFromWindow(mainWebView.getWindowToken(), 0); - // Clear the focus from from the URL text box. + // Clear the focus from from the URL text box and the WebView. This removes any text selection markers and context menues, which otherwise draw above the open drawers. urlTextBox.clearFocus(); + mainWebView.clearFocus(); } } }); @@ -1147,32 +1142,37 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Allow the downloading of files. mainWebView.setDownloadListener((String url, String userAgent, String contentDisposition, String mimetype, long contentLength) -> { - // Check to see if the WRITE_EXTERNAL_STORAGE permission has already been granted. - if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) { - // The WRITE_EXTERNAL_STORAGE permission needs to be requested. - - // Store the variables for future use by `onRequestPermissionsResult()`. - downloadUrl = url; - downloadContentDisposition = contentDisposition; - downloadContentLength = contentLength; - - // Show a dialog if the user has previously denied the permission. - if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { // Show a dialog explaining the request first. - // Instantiate the download location permission alert dialog and set the download type to DOWNLOAD_FILE. - DialogFragment downloadLocationPermissionDialogFragment = DownloadLocationPermissionDialog.downloadType(DownloadLocationPermissionDialog.DOWNLOAD_FILE); - - // Show the download location permission alert dialog. The permission will be requested when the the dialog is closed. - downloadLocationPermissionDialogFragment.show(getFragmentManager(), getString(R.string.download_location)); - } else { // Show the permission request directly. - // Request the permission. The download dialog will be launched by `onRequestPermissionResult()`. - ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, DOWNLOAD_FILE_REQUEST_CODE); - } - } else { // The storage permission has already been granted. - // Get a handle for the download file alert dialog. - AppCompatDialogFragment downloadFileDialogFragment = DownloadFileDialog.fromUrl(url, contentDisposition, contentLength); + // Check if the download should be processed by an external app. + if (downloadWithExternalApp) { // Download with an external app. + openUrlWithExternalApp(url); + } else { // Download with Android's download manager. + // Check to see if the WRITE_EXTERNAL_STORAGE permission has already been granted. + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) { // The storage permission has not been granted. + // The WRITE_EXTERNAL_STORAGE permission needs to be requested. + + // Store the variables for future use by `onRequestPermissionsResult()`. + downloadUrl = url; + downloadContentDisposition = contentDisposition; + downloadContentLength = contentLength; + + // Show a dialog if the user has previously denied the permission. + if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { // Show a dialog explaining the request first. + // Instantiate the download location permission alert dialog and set the download type to DOWNLOAD_FILE. + DialogFragment downloadLocationPermissionDialogFragment = DownloadLocationPermissionDialog.downloadType(DownloadLocationPermissionDialog.DOWNLOAD_FILE); + + // Show the download location permission alert dialog. The permission will be requested when the the dialog is closed. + downloadLocationPermissionDialogFragment.show(getFragmentManager(), getString(R.string.download_location)); + } else { // Show the permission request directly. + // Request the permission. The download dialog will be launched by `onRequestPermissionResult()`. + ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, DOWNLOAD_FILE_REQUEST_CODE); + } + } else { // The storage permission has already been granted. + // Get a handle for the download file alert dialog. + AppCompatDialogFragment downloadFileDialogFragment = DownloadFileDialog.fromUrl(url, contentDisposition, contentLength); - // Show the download file alert dialog. - downloadFileDialogFragment.show(getSupportFragmentManager(), getString(R.string.download)); + // Show the download file alert dialog. + downloadFileDialogFragment.show(getSupportFragmentManager(), getString(R.string.download)); + } } }); @@ -1247,7 +1247,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } // Initialize the user agent array adapter and string array. - userAgentNamesArray = ArrayAdapter.createFromResource(this, R.array.user_agent_names, R.layout.domain_settings_spinner_item); + userAgentNamesArray = ArrayAdapter.createFromResource(this, R.array.user_agent_names, R.layout.spinner_item); userAgentDataArray = getResources().getStringArray(R.array.user_agent_data); // Apply the app settings from the shared preferences. @@ -1287,10 +1287,19 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook formattedUrlString = ""; // Apply the domain settings for the new URL. `applyDomainSettings` doesn't do anything if the domain has not changed. - applyDomainSettings(url, true, false); + boolean userAgentChanged = applyDomainSettings(url, true, false); - // Returning false causes the current WebView to handle the URL and prevents it from adding redirects to the history list. - return false; + // Check if the user agent has changed. + if (userAgentChanged) { + // Manually load the URL. The changing of the user agent will cause WebView to reload the previous URL. + mainWebView.loadUrl(url, customHeaders); + + // Returning true indicates that Privacy Browser is manually handling the loading of the URL. + return true; + } else { + // Returning false causes the current WebView to handle the URL and prevents it from adding redirects to the history list. + return false; + } } else 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); @@ -1585,11 +1594,16 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Apply any custom domain settings if the URL was loaded by navigating history. if (navigatingHistory) { + // Apply the domain settings. + boolean userAgentChanged = applyDomainSettings(url, true, false); + // Reset `navigatingHistory`. navigatingHistory = false; - // Apply the domain settings. - applyDomainSettings(url, true, false); + // Manually load the URL if the user agent has changed, which will have caused the previous URL to be reloaded. + if (userAgentChanged) { + loadUrl(formattedUrlString); + } } // Set `urlIsLoading` to `true`, so that redirects while loading do not trigger changes in the user agent, which forces another reload of the existing page. @@ -1699,7 +1713,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook sslCertificate = mainWebView.getCertificate(); // Check the current website SSL certificate against the pinned SSL certificate if there is a pinned SSL certificate the user has not chosen to ignore it for this session. - if (pinnedDomainSslCertificate && !ignorePinnedSslCertificate) { + // Also ignore if changes in the user agent causes an error while navigating history. + if (pinnedDomainSslCertificate && !ignorePinnedSslCertificate && navigatingHistory) { // Initialize the current SSL certificate variables. String currentWebsiteIssuedToCName = ""; String currentWebsiteIssuedToOName = ""; @@ -2725,9 +2740,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } else { // Images are not currently loaded automatically. mainWebView.getSettings().setLoadsImagesAutomatically(true); } - - // Set `onTheFlyDisplayImagesSet`. - onTheFlyDisplayImagesSet = true; return true; case R.id.night_mode: @@ -2746,7 +2758,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); // Get the JavaScript preference. - javaScriptEnabled = sharedPreferences.getBoolean("javascript_enabled", false); + javaScriptEnabled = sharedPreferences.getBoolean("javascript", false); } // Apply the JavaScript setting to the WebView. @@ -2792,8 +2804,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook String shareString = webViewTitle + " – " + urlTextBox.getText().toString(); // Create the share intent. - Intent shareIntent = new Intent(); - shareIntent.setAction(Intent.ACTION_SEND); + Intent shareIntent = new Intent(Intent.ACTION_SEND); shareIntent.putExtra(Intent.EXTRA_TEXT, shareString); shareIntent.setType("text/plain"); @@ -3145,32 +3156,35 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Add a Download URL entry. menu.add(R.string.download_url).setOnMenuItemClickListener((MenuItem item) -> { - // Check to see if the WRITE_EXTERNAL_STORAGE permission has already been granted. - if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) { - // The WRITE_EXTERNAL_STORAGE permission needs to be requested. - - // Store the variables for future use by `onRequestPermissionsResult()`. - downloadUrl = linkUrl; - downloadContentDisposition = "none"; - downloadContentLength = -1; - - // Show a dialog if the user has previously denied the permission. - if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { // Show a dialog explaining the request first. - // Instantiate the download location permission alert dialog and set the download type to DOWNLOAD_FILE. - DialogFragment downloadLocationPermissionDialogFragment = DownloadLocationPermissionDialog.downloadType(DownloadLocationPermissionDialog.DOWNLOAD_FILE); - - // Show the download location permission alert dialog. The permission will be requested when the the dialog is closed. - downloadLocationPermissionDialogFragment.show(getFragmentManager(), getString(R.string.download_location)); - } else { // Show the permission request directly. - // Request the permission. The download dialog will be launched by `onRequestPermissionResult()`. - ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, DOWNLOAD_FILE_REQUEST_CODE); - } - } else { // The WRITE_EXTERNAL_STORAGE permission has already been granted. - // Get a handle for the download file alert dialog. - AppCompatDialogFragment downloadFileDialogFragment = DownloadFileDialog.fromUrl(linkUrl, "none", -1); + // Check if the download should be processed by an external app. + if (downloadWithExternalApp) { // Download with an external app. + openUrlWithExternalApp(linkUrl); + } else { // Download with Android's download manager. + // Check to see if the storage permission has already been granted. + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) { // The storage permission needs to be requested. + // Store the variables for future use by `onRequestPermissionsResult()`. + downloadUrl = linkUrl; + downloadContentDisposition = "none"; + downloadContentLength = -1; + + // Show a dialog if the user has previously denied the permission. + if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { // Show a dialog explaining the request first. + // Instantiate the download location permission alert dialog and set the download type to DOWNLOAD_FILE. + DialogFragment downloadLocationPermissionDialogFragment = DownloadLocationPermissionDialog.downloadType(DownloadLocationPermissionDialog.DOWNLOAD_FILE); + + // Show the download location permission alert dialog. The permission will be requested when the the dialog is closed. + downloadLocationPermissionDialogFragment.show(getFragmentManager(), getString(R.string.download_location)); + } else { // Show the permission request directly. + // Request the permission. The download dialog will be launched by `onRequestPermissionResult()`. + ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, DOWNLOAD_FILE_REQUEST_CODE); + } + } else { // The storage permission has already been granted. + // Get a handle for the download file alert dialog. + AppCompatDialogFragment downloadFileDialogFragment = DownloadFileDialog.fromUrl(linkUrl, "none", -1); - // Show the download file alert dialog. - downloadFileDialogFragment.show(getSupportFragmentManager(), getString(R.string.download)); + // Show the download file alert dialog. + downloadFileDialogFragment.show(getSupportFragmentManager(), getString(R.string.download)); + } } return false; }); @@ -3188,7 +3202,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Add a `Write Email` entry. menu.add(R.string.write_email).setOnMenuItemClickListener(item -> { - // We use `ACTION_SENDTO` instead of `ACTION_SEND` so that only email programs are launched. + // 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`. @@ -3232,30 +3246,33 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Add a `Download Image` entry. menu.add(R.string.download_image).setOnMenuItemClickListener((MenuItem item) -> { - // Check to see if the WRITE_EXTERNAL_STORAGE permission has already been granted. - if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) { - // The WRITE_EXTERNAL_STORAGE permission needs to be requested. - - // Store the image URL for use by `onRequestPermissionResult()`. - downloadImageUrl = imageUrl; - - // Show a dialog if the user has previously denied the permission. - if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { // Show a dialog explaining the request first. - // Instantiate the download location permission alert dialog and set the download type to DOWNLOAD_IMAGE. - DialogFragment downloadLocationPermissionDialogFragment = DownloadLocationPermissionDialog.downloadType(DownloadLocationPermissionDialog.DOWNLOAD_IMAGE); - - // Show the download location permission alert dialog. The permission will be requested when the dialog is closed. - downloadLocationPermissionDialogFragment.show(getFragmentManager(), getString(R.string.download_location)); - } else { // Show the permission request directly. - // Request the permission. The download dialog will be launched by `onRequestPermissionResult(). - ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, DOWNLOAD_IMAGE_REQUEST_CODE); - } - } else { // The WRITE_EXTERNAL_STORAGE permission has already been granted. - // Get a handle for the download image alert dialog. - AppCompatDialogFragment downloadImageDialogFragment = DownloadImageDialog.imageUrl(imageUrl); + // Check if the download should be processed by an external app. + if (downloadWithExternalApp) { // Download with an external app. + openUrlWithExternalApp(imageUrl); + } else { // Download with Android's download manager. + // Check to see if the storage permission has already been granted. + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) { // The storage permission needs to be requested. + // Store the image URL for use by `onRequestPermissionResult()`. + downloadImageUrl = imageUrl; + + // Show a dialog if the user has previously denied the permission. + if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { // Show a dialog explaining the request first. + // Instantiate the download location permission alert dialog and set the download type to DOWNLOAD_IMAGE. + DialogFragment downloadLocationPermissionDialogFragment = DownloadLocationPermissionDialog.downloadType(DownloadLocationPermissionDialog.DOWNLOAD_IMAGE); + + // Show the download location permission alert dialog. The permission will be requested when the dialog is closed. + downloadLocationPermissionDialogFragment.show(getFragmentManager(), getString(R.string.download_location)); + } else { // Show the permission request directly. + // Request the permission. The download dialog will be launched by `onRequestPermissionResult(). + ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, DOWNLOAD_IMAGE_REQUEST_CODE); + } + } else { // The storage permission has already been granted. + // Get a handle for the download image alert dialog. + AppCompatDialogFragment downloadImageDialogFragment = DownloadImageDialog.imageUrl(imageUrl); - // Show the download image alert dialog. - downloadImageDialogFragment.show(getSupportFragmentManager(), getString(R.string.download)); + // Show the download image alert dialog. + downloadImageDialogFragment.show(getSupportFragmentManager(), getString(R.string.download)); + } } return false; }); @@ -3291,30 +3308,33 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Add a `Download Image` entry. menu.add(R.string.download_image).setOnMenuItemClickListener((MenuItem item) -> { - // Check to see if the WRITE_EXTERNAL_STORAGE permission has already been granted. - if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) { - // The WRITE_EXTERNAL_STORAGE permission needs to be requested. - - // Store the image URL for use by `onRequestPermissionResult()`. - downloadImageUrl = imageUrl; - - // Show a dialog if the user has previously denied the permission. - if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { // Show a dialog explaining the request first. - // Instantiate the download location permission alert dialog and set the download type to DOWNLOAD_IMAGE. - DialogFragment downloadLocationPermissionDialogFragment = DownloadLocationPermissionDialog.downloadType(DownloadLocationPermissionDialog.DOWNLOAD_IMAGE); - - // Show the download location permission alert dialog. The permission will be requested when the dialog is closed. - downloadLocationPermissionDialogFragment.show(getFragmentManager(), getString(R.string.download_location)); - } else { // Show the permission request directly. - // Request the permission. The download dialog will be launched by `onRequestPermissionResult(). - ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, DOWNLOAD_IMAGE_REQUEST_CODE); - } - } else { // The WRITE_EXTERNAL_STORAGE permission has already been granted. - // Get a handle for the download image alert dialog. - AppCompatDialogFragment downloadImageDialogFragment = DownloadImageDialog.imageUrl(imageUrl); + // Check if the download should be processed by an external app. + if (downloadWithExternalApp) { // Download with an external app. + openUrlWithExternalApp(imageUrl); + } else { // Download with Android's download manager. + // Check to see if the storage permission has already been granted. + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) { // The storage permission needs to be requested. + // Store the image URL for use by `onRequestPermissionResult()`. + downloadImageUrl = imageUrl; + + // Show a dialog if the user has previously denied the permission. + if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { // Show a dialog explaining the request first. + // Instantiate the download location permission alert dialog and set the download type to DOWNLOAD_IMAGE. + DialogFragment downloadLocationPermissionDialogFragment = DownloadLocationPermissionDialog.downloadType(DownloadLocationPermissionDialog.DOWNLOAD_IMAGE); + + // Show the download location permission alert dialog. The permission will be requested when the dialog is closed. + downloadLocationPermissionDialogFragment.show(getFragmentManager(), getString(R.string.download_location)); + } else { // Show the permission request directly. + // Request the permission. The download dialog will be launched by `onRequestPermissionResult(). + ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, DOWNLOAD_IMAGE_REQUEST_CODE); + } + } else { // The storage permission has already been granted. + // Get a handle for the download image alert dialog. + AppCompatDialogFragment downloadImageDialogFragment = DownloadImageDialog.imageUrl(imageUrl); - // Show the download image alert dialog. - downloadImageDialogFragment.show(getSupportFragmentManager(), getString(R.string.download)); + // Show the download image alert dialog. + downloadImageDialogFragment.show(getSupportFragmentManager(), getString(R.string.download)); + } } return false; }); @@ -3421,8 +3441,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook IconCompat favoriteIcon = IconCompat.createWithBitmap(favoriteIconBitmap); // Setup the shortcut intent. - Intent shortcutIntent = new Intent(); - shortcutIntent.setAction(Intent.ACTION_VIEW); + Intent shortcutIntent = new Intent(Intent.ACTION_VIEW); shortcutIntent.setData(Uri.parse(formattedUrlString)); // Create a shortcut info builder. The shortcut name becomes the shortcut ID. @@ -3906,7 +3925,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook fullScreenBrowsingModeEnabled = sharedPreferences.getBoolean("full_screen_browsing_mode", false); hideSystemBarsOnFullscreen = sharedPreferences.getBoolean("hide_system_bars", false); translucentNavigationBarOnFullscreen = sharedPreferences.getBoolean("translucent_navigation_bar", true); - displayWebpageImagesBoolean = sharedPreferences.getBoolean("display_webpage_images", true); + downloadWithExternalApp = sharedPreferences.getBoolean("download_with_external_app", false); // Apply the proxy through Orbot settings. applyProxyThroughOrbot(false); @@ -3982,7 +4001,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `reloadWebsite` is used if returning from the Domains activity. Otherwise JavaScript might not function correctly if it is newly enabled. // The deprecated `.getDrawable()` must be used until the minimum API >= 21. @SuppressWarnings("deprecation") - private void applyDomainSettings(String url, boolean resetFavoriteIcon, boolean reloadWebsite) { + private boolean applyDomainSettings(String url, boolean resetFavoriteIcon, boolean reloadWebsite) { + // Get the current user agent. + String initialUserAgent = mainWebView.getSettings().getUserAgentString(); + + // Initialize a variable to track if the user agent changes. + boolean userAgentChanged = false; + // Parse the URL into a URI. Uri uri = Uri.parse(url); @@ -4073,13 +4098,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); // Store the general preference information. - String defaultFontSizeString = sharedPreferences.getString("default_font_size", "100"); - String defaultUserAgentName = sharedPreferences.getString("user_agent", "Privacy Browser"); - defaultCustomUserAgentString = sharedPreferences.getString("custom_user_agent", "PrivacyBrowser/1.0"); + String defaultFontSizeString = sharedPreferences.getString("font_size", getString(R.string.font_size_default_value)); + String defaultUserAgentName = sharedPreferences.getString("user_agent", getString(R.string.user_agent_default_value)); + defaultCustomUserAgentString = sharedPreferences.getString("custom_user_agent", getString(R.string.custom_user_agent_default_value)); boolean defaultSwipeToRefresh = sharedPreferences.getBoolean("swipe_to_refresh", true); nightMode = sharedPreferences.getBoolean("night_mode", false); + boolean displayWebpageImages = sharedPreferences.getBoolean("display_webpage_images", true); - if (domainSettingsApplied) { // The url we are loading has custom domain settings. + if (domainSettingsApplied) { // The url has custom domain settings. // Get a cursor for the current host and move it to the first position. Cursor currentHostDomainSettingsCursor = domainsDatabaseHelper.getCursorForDomainName(domainNameInDatabase); currentHostDomainSettingsCursor.moveToFirst(); @@ -4102,7 +4128,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook int fontSize = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.FONT_SIZE)); int swipeToRefreshInt = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SWIPE_TO_REFRESH)); int nightModeInt = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.NIGHT_MODE)); - displayWebpageImagesInt = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.DISPLAY_IMAGES)); + int displayWebpageImagesInt = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.DISPLAY_IMAGES)); pinnedDomainSslCertificate = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE)) == 1); pinnedDomainSslIssuedToCNameString = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME)); pinnedDomainSslIssuedToONameString = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION)); @@ -4218,25 +4244,43 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } } - // Set swipe to refresh. - switch (swipeToRefreshInt) { - case DomainsDatabaseHelper.SWIPE_TO_REFRESH_SYSTEM_DEFAULT: - // Set swipe to refresh according to the default. - swipeRefreshLayout.setEnabled(defaultSwipeToRefresh); - break; + // Store the applied user agent string, which is used in the View Source activity. + appliedUserAgentString = mainWebView.getSettings().getUserAgentString(); - case DomainsDatabaseHelper.SWIPE_TO_REFRESH_ENABLED: - // Enable swipe to refresh. - swipeRefreshLayout.setEnabled(true); - break; + // Update the user agent change tracker. + userAgentChanged = !appliedUserAgentString.equals(initialUserAgent); + } - case DomainsDatabaseHelper.SWIPE_TO_REFRESH_DISABLED: - // Disable swipe to refresh. - swipeRefreshLayout.setEnabled(false); - } + // Set swipe to refresh. + switch (swipeToRefreshInt) { + case DomainsDatabaseHelper.SWIPE_TO_REFRESH_SYSTEM_DEFAULT: + // Set swipe to refresh according to the default. + swipeRefreshLayout.setEnabled(defaultSwipeToRefresh); + break; - // Store the applied user agent string, which is used in the View Source activity. - appliedUserAgentString = mainWebView.getSettings().getUserAgentString(); + case DomainsDatabaseHelper.SWIPE_TO_REFRESH_ENABLED: + // Enable swipe to refresh. + swipeRefreshLayout.setEnabled(true); + break; + + case DomainsDatabaseHelper.SWIPE_TO_REFRESH_DISABLED: + // Disable swipe to refresh. + swipeRefreshLayout.setEnabled(false); + } + + // Set the loading of webpage images. + switch (displayWebpageImagesInt) { + case DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_SYSTEM_DEFAULT: + mainWebView.getSettings().setLoadsImagesAutomatically(displayWebpageImages); + break; + + case DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_ENABLED: + mainWebView.getSettings().setLoadsImagesAutomatically(true); + break; + + case DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_DISABLED: + mainWebView.getSettings().setLoadsImagesAutomatically(false); + break; } // 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. @@ -4247,15 +4291,15 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } } else { // The new URL does not have custom domain settings. Load the defaults. // Store the values from `sharedPreferences` in variables. - javaScriptEnabled = sharedPreferences.getBoolean("javascript_enabled", false); - firstPartyCookiesEnabled = sharedPreferences.getBoolean("first_party_cookies_enabled", false); - thirdPartyCookiesEnabled = sharedPreferences.getBoolean("third_party_cookies_enabled", false); - domStorageEnabled = sharedPreferences.getBoolean("dom_storage_enabled", false); - saveFormDataEnabled = sharedPreferences.getBoolean("save_form_data_enabled", false); // Form data can be removed once the minimum API >= 26. + javaScriptEnabled = sharedPreferences.getBoolean("javascript", false); + firstPartyCookiesEnabled = sharedPreferences.getBoolean("first_party_cookies", false); + thirdPartyCookiesEnabled = sharedPreferences.getBoolean("third_party_cookies", false); + domStorageEnabled = sharedPreferences.getBoolean("dom_storage", false); + saveFormDataEnabled = sharedPreferences.getBoolean("save_form_data", false); // Form data can be removed once the minimum API >= 26. easyListEnabled = sharedPreferences.getBoolean("easylist", true); easyPrivacyEnabled = sharedPreferences.getBoolean("easyprivacy", true); - fanboysAnnoyanceListEnabled = sharedPreferences.getBoolean("fanboy_annoyance_list", true); - fanboysSocialBlockingListEnabled = sharedPreferences.getBoolean("fanboy_social_blocking_list", true); + fanboysAnnoyanceListEnabled = sharedPreferences.getBoolean("fanboys_annoyance_list", true); + fanboysSocialBlockingListEnabled = sharedPreferences.getBoolean("fanboys_social_blocking_list", true); ultraPrivacyEnabled = sharedPreferences.getBoolean("ultraprivacy", true); blockAllThirdPartyRequests = sharedPreferences.getBoolean("block_all_third_party_requests", false); @@ -4323,8 +4367,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Store the applied user agent string, which is used in the View Source activity. appliedUserAgentString = mainWebView.getSettings().getUserAgentString(); + + // Update the user agent change tracker. + userAgentChanged = !appliedUserAgentString.equals(initialUserAgent); } + // Set the loading of webpage images. + mainWebView.getSettings().setLoadsImagesAutomatically(displayWebpageImages); + // Set a transparent background on `urlTextBox`. We have to use the deprecated `.getDrawable()` until the minimum API >= 21. urlAppBarRelativeLayout.setBackgroundDrawable(getResources().getDrawable(R.color.transparent)); } @@ -4332,10 +4382,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Close the domains database helper. domainsDatabaseHelper.close(); - // Remove the `onTheFlyDisplayImagesSet` flag and set the display webpage images mode. `true` indicates that custom domain settings are applied. - onTheFlyDisplayImagesSet = false; - setDisplayWebpageImages(); - // Update the privacy icons, but only if `mainMenu` has already been populated. if (mainMenu != null) { updatePrivacyIcons(true); @@ -4346,6 +4392,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook if (reloadWebsite) { mainWebView.reload(); } + + // Return the user agent changed status. + return userAgentChanged; } private void applyProxyThroughOrbot(boolean reloadWebsite) { @@ -4353,12 +4402,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); // Get the search preferences. - String homepageString = sharedPreferences.getString("homepage", "https://searx.me/"); - String torHomepageString = sharedPreferences.getString("tor_homepage", "http://ulrn6sryqaifefld.onion/"); - String torSearchString = sharedPreferences.getString("tor_search", "http://ulrn6sryqaifefld.onion/?q="); - String torSearchCustomUrlString = sharedPreferences.getString("tor_search_custom_url", ""); - String searchString = sharedPreferences.getString("search", "https://searx.me/?q="); - String searchCustomUrlString = sharedPreferences.getString("search_custom_url", ""); + String homepageString = sharedPreferences.getString("homepage", getString(R.string.homepage_default_value)); + String torHomepageString = sharedPreferences.getString("tor_homepage", getString(R.string.tor_homepage_default_value)); + String torSearchString = sharedPreferences.getString("tor_search", getString(R.string.tor_search_default_value)); + String torSearchCustomUrlString = sharedPreferences.getString("tor_search_custom_url", getString(R.string.tor_search_custom_url_default_value)); + String searchString = sharedPreferences.getString("search", getString(R.string.search_default_value)); + String searchCustomUrlString = sharedPreferences.getString("search_custom_url", getString(R.string.search_custom_url_default_value)); // Set the homepage, search, and proxy options. if (proxyThroughOrbot) { // Set the Tor options. @@ -4437,28 +4486,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } } - private void setDisplayWebpageImages() { - if (!onTheFlyDisplayImagesSet) { - if (domainSettingsApplied) { // Custom domain settings are applied. - switch (displayWebpageImagesInt) { - case DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_SYSTEM_DEFAULT: - mainWebView.getSettings().setLoadsImagesAutomatically(displayWebpageImagesBoolean); - break; - - case DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_ENABLED: - mainWebView.getSettings().setLoadsImagesAutomatically(true); - break; - - case DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_DISABLED: - mainWebView.getSettings().setLoadsImagesAutomatically(false); - break; - } - } else { // Default settings are applied. - mainWebView.getSettings().setLoadsImagesAutomatically(displayWebpageImagesBoolean); - } - } - } - private void updatePrivacyIcons(boolean runInvalidateOptionsMenu) { // Get handles for the menu items. MenuItem privacyMenuItem = mainMenu.findItem(R.id.toggle_javascript); @@ -4516,18 +4543,63 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } } + private void openUrlWithExternalApp(String url) { + // Create a download intent. Not specifying the action type will display the maximum number of options. + Intent downloadIntent = new Intent(); + + // Set the URI and the mime type. `"*/*"` will display the maximum number of options. + downloadIntent.setDataAndType(Uri.parse(url), "text/html"); + + // Flag the intent to open in a new task. + downloadIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + // Show the chooser. + startActivity(Intent.createChooser(downloadIntent, getString(R.string.open_with))); + } + private void highlightUrlText() { + // Get the URL string. String urlString = urlTextBox.getText().toString(); + // Get the index of the `/` immediately after the domain name. + int endOfDomainName = urlString.indexOf("/", (urlString.indexOf("//") + 2)); + + // Create a base URL string. + String baseUrl; + + // Get the base URL. + if (endOfDomainName > 0) { // There is at least one character after the base URL. + // Get the base URL. + baseUrl = urlString.substring(0, endOfDomainName); + } else { // There are no characters after the base URL. + // Set the base URL to be the entire URL string. + baseUrl = urlString; + } + + // Get the index of the last `.` in the domain. + int lastDotIndex = baseUrl.lastIndexOf("."); + + // Get the index of the penultimate `.` in the domain. + int penultimateDotIndex = baseUrl.lastIndexOf(".", lastDotIndex - 1); + + // Markup the beginning of the URL. if (urlString.startsWith("http://")) { // Highlight the protocol of connections that are not encrypted. urlTextBox.getText().setSpan(redColorSpan, 0, 7, Spanned.SPAN_INCLUSIVE_INCLUSIVE); + + // De-emphasize subdomains. + if (penultimateDotIndex > 0) { // There is more than one subdomain in the domain name. + urlTextBox.getText().setSpan(initialGrayColorSpan, 7, penultimateDotIndex + 1, Spanned.SPAN_INCLUSIVE_INCLUSIVE); + } } else if (urlString.startsWith("https://")) { // De-emphasize the protocol of connections that are encrypted. - urlTextBox.getText().setSpan(initialGrayColorSpan, 0, 8, Spanned.SPAN_INCLUSIVE_INCLUSIVE); + if (penultimateDotIndex > 0) { // There is more than one subdomain in the domain name. + // De-emphasize the protocol and the additional subdomains. + urlTextBox.getText().setSpan(initialGrayColorSpan, 0, penultimateDotIndex + 1, Spanned.SPAN_INCLUSIVE_INCLUSIVE); + } else { // There is only one subdomain in the domain name. + // De-emphasize only the protocol. + urlTextBox.getText().setSpan(initialGrayColorSpan, 0, 8, Spanned.SPAN_INCLUSIVE_INCLUSIVE); + } } - // Get the index of the `/` immediately after the domain name. - int endOfDomainName = urlString.indexOf("/", (urlString.indexOf("//") + 2)); - // De-emphasize the text after the domain name. if (endOfDomainName > 0) { urlTextBox.getText().setSpan(finalGrayColorSpan, endOfDomainName, urlString.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);