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=3d92e076e370a669d49b927146abe662f56c3d8e;hp=7c13950a9334030c6d4d86a608046e4508975e96;hb=6a85cc5ca039054a24c4601de785e0bc1a234722;hpb=de29e5f5b80d44fdc50bbb21379db860a7c7dfa2
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 7c13950a..3d92e076 100644
--- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java
+++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java
@@ -26,6 +26,7 @@ import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.DialogFragment;
import android.app.DownloadManager;
+import android.app.SearchManager;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ClipData;
@@ -36,6 +37,7 @@ import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -167,14 +169,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
public static Bitmap favoriteIconBitmap;
// `formattedUrlString` is public static so it can be accessed from `BookmarksActivity`, `CreateBookmarkDialog`, and `AddDomainDialog`.
- // It is also used in `onCreate()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onCreateHomeScreenShortcutCreate()`, and `loadUrlFromTextBox()`.
+ // It is also used in `onCreate()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onCreateHomeScreenShortcutCreate()`, `loadUrlFromTextBox()`, and `applyProxyThroughOrbot()`.
public static String formattedUrlString;
// `sslCertificate` is public static so it can be accessed from `DomainsActivity`, `DomainsListFragment`, `DomainSettingsFragment`, `PinnedSslCertificateMismatchDialog`,
// and `ViewSslCertificateDialog`. It is also used in `onCreate()`.
public static SslCertificate sslCertificate;
- // `orbotStatus` is public static so it can be accessed from `OrbotProxyHelper`. It is also used in `onCreate()` and `onResume()`.
+ // `orbotStatus` is public static so it can be accessed from `OrbotProxyHelper`. It is also used in `onCreate()`, `onResume()`, and `applyProxyThroughOrbot()`.
public static String orbotStatus;
// `webViewTitle` is public static so it can be accessed from `CreateBookmarkDialog` and `CreateHomeScreenShortcutDialog`. It is also used in `onCreate()`.
@@ -276,7 +278,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
public final static int DOMAINS_CUSTOM_USER_AGENT = 13;
- // `appBar` is used in `onCreate()`, `onOptionsItemSelected()`, `closeFindOnPage()`, and `applyAppSettings()`.
+ // `appBar` is used in `onCreate()`, `onOptionsItemSelected()`, `closeFindOnPage()`, `applyAppSettings()`, and `applyProxyThroughOrbot()`.
private ActionBar appBar;
// `navigatingHistory` is used in `onCreate()`, `onNavigationItemSelected()`, `onSslMismatchBack()`, and `applyDomainSettings()`.
@@ -292,7 +294,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()`, and `setDisplayWebpageImages()`.
+ // `findNextOnPage()`, `closeFindOnPage()`, `loadUrlFromTextBox()`, `onSslMismatchBack()`, and `applyProxyThroughOrbot()`.
private WebView mainWebView;
// `fullScreenVideoFrameLayout` is used in `onCreate()` and `onConfigurationChanged()`.
@@ -331,13 +333,10 @@ 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 `applyAppSettings()`.
+ // 'homepage' is used in `onCreate()`, `onNavigationItemSelected()`, and `applyProxyThroughOrbot()`.
private String homepage;
- // `searchURL` is used in `loadURLFromTextBox()` and `applyAppSettings()`.
+ // `searchURL` is used in `loadURLFromTextBox()` and `applyProxyThroughOrbot()`.
private String searchURL;
// `mainMenu` is used in `onCreateOptionsMenu()` and `updatePrivacyIcons()`.
@@ -371,7 +370,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
// `privacyBrowserRuntime` is used in `onCreate()`, `onOptionsItemSelected()`, and `applyAppSettings()`.
private Runtime privacyBrowserRuntime;
- // `proxyThroughOrbot` is used in `onRestart()` and `applyAppSettings()`.
+ // `proxyThroughOrbot` is used in `onRestart()`, `onOptionsItemSelected()`, `applyAppSettings()`, and `applyProxyThroughOrbot()`.
private boolean proxyThroughOrbot;
// `incognitoModeEnabled` is used in `onCreate()` and `applyAppSettings()`.
@@ -398,6 +397,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;
@@ -407,23 +409,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
// `orbotStatusBroadcastReceiver` is used in `onCreate()` and `onDestroy()`.
private BroadcastReceiver orbotStatusBroadcastReceiver;
- // `waitingForOrbot` is used in `onCreate()`, `onResume()`, and `applyAppSettings()`.
+ // `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;
-
- // `waitingForOrbotData` is used in `onCreate()` and `applyAppSettings()`.
- private String waitingForOrbotHTMLString;
+ // `waitingForOrbotHtmlString` is used in `onCreate()` and `applyProxyThroughOrbot()`.
+ private String waitingForOrbotHtmlString;
// `privateDataDirectoryString` is used in `onCreate()`, `onOptionsItemSelected()`, and `onNavigationItemSelected()`.
private String privateDataDirectoryString;
@@ -511,7 +507,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
// Remove Android Studio's warning about the dangers of using SetJavaScriptEnabled. The whole premise of Privacy Browser is built around an understanding of these dangers.
// Also, remove the warning about needing to override `performClick()` when using an `OnTouchListener` with `WebView`.
@SuppressLint({"SetJavaScriptEnabled", "ClickableViewAccessibility"})
- // Remove Android Studio's warning about deprecations. We have to use the deprecated `getColor()` until API >= 23.
+ // Remove Android Studio's warning about deprecations. The deprecated `getColor()` must be used until API >= 23.
@SuppressWarnings("deprecation")
protected void onCreate(Bundle savedInstanceState) {
// Get a handle for the shared preferences.
@@ -539,6 +535,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
// Set the content view.
setContentView(R.layout.main_drawerlayout);
+ // Get a handle for the resources.
+ Resources resources = getResources();
+
// Get a handle for `inputMethodManager`.
inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
@@ -555,9 +554,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
appBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
// 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));
+ redColorSpan = new ForegroundColorSpan(resources.getColor(R.color.red_a700));
+ initialGrayColorSpan = new ForegroundColorSpan(resources.getColor(R.color.gray_500));
+ finalGrayColorSpan = new ForegroundColorSpan(resources.getColor(R.color.gray_500));
// Get a handle for `urlTextBox`.
urlTextBox = findViewById(R.id.url_edittext);
@@ -583,11 +582,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
// 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.
- try {
- loadUrlFromTextBox();
- } catch (UnsupportedEncodingException e) {
- e.printStackTrace();
- }
+ loadUrlFromTextBox();
+
// If the enter key was pressed, consume the event.
return true;
} else {
@@ -597,7 +593,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
});
// Set `waitingForOrbotHTMLString`.
- waitingForOrbotHTMLString = "
" + getString(R.string.waiting_for_orbot) + "
";
+ waitingForOrbotHtmlString = "
" + getString(R.string.waiting_for_orbot) + "
";
// Initialize `currentDomainName`, `orbotStatus`, and `waitingForOrbot`.
currentDomainName = "";
@@ -643,15 +639,15 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
// Set the bookmarks drawer resources according to the theme. This can't be done in the layout due to compatibility issues with the `DrawerLayout` support widget.
if (darkTheme) {
- launchBookmarksActivityFab.setImageDrawable(getResources().getDrawable(R.drawable.bookmarks_dark));
- createBookmarkFolderFab.setImageDrawable(getResources().getDrawable(R.drawable.create_folder_dark));
- createBookmarkFab.setImageDrawable(getResources().getDrawable(R.drawable.create_bookmark_dark));
- bookmarksListView.setBackgroundColor(getResources().getColor(R.color.gray_850));
+ launchBookmarksActivityFab.setImageDrawable(resources.getDrawable(R.drawable.bookmarks_dark));
+ createBookmarkFolderFab.setImageDrawable(resources.getDrawable(R.drawable.create_folder_dark));
+ createBookmarkFab.setImageDrawable(resources.getDrawable(R.drawable.create_bookmark_dark));
+ bookmarksListView.setBackgroundColor(resources.getColor(R.color.gray_850));
} else {
- launchBookmarksActivityFab.setImageDrawable(getResources().getDrawable(R.drawable.bookmarks_light));
- createBookmarkFolderFab.setImageDrawable(getResources().getDrawable(R.drawable.create_folder_light));
- createBookmarkFab.setImageDrawable(getResources().getDrawable(R.drawable.create_bookmark_light));
- bookmarksListView.setBackgroundColor(getResources().getColor(R.color.white));
+ launchBookmarksActivityFab.setImageDrawable(resources.getDrawable(R.drawable.bookmarks_light));
+ createBookmarkFolderFab.setImageDrawable(resources.getDrawable(R.drawable.create_folder_light));
+ createBookmarkFab.setImageDrawable(resources.getDrawable(R.drawable.create_bookmark_light));
+ bookmarksListView.setBackgroundColor(resources.getColor(R.color.white));
}
// Set the launch bookmarks activity FAB to launch the bookmarks activity.
@@ -670,14 +666,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
createBookmarkFolderFab.setOnClickListener(v -> {
// Show the `CreateBookmarkFolderDialog` `AlertDialog` and name the instance `@string/create_folder`.
AppCompatDialogFragment createBookmarkFolderDialog = new CreateBookmarkFolderDialog();
- createBookmarkFolderDialog.show(getSupportFragmentManager(), getResources().getString(R.string.create_folder));
+ createBookmarkFolderDialog.show(getSupportFragmentManager(), resources.getString(R.string.create_folder));
});
// Set the create new bookmark FAB to display an alert dialog.
createBookmarkFab.setOnClickListener(view -> {
// Show the `CreateBookmarkDialog` `AlertDialog` and name the instance `@string/create_bookmark`.
AppCompatDialogFragment createBookmarkDialog = new CreateBookmarkDialog();
- createBookmarkDialog.show(getSupportFragmentManager(), getResources().getString(R.string.create_bookmark));
+ createBookmarkDialog.show(getSupportFragmentManager(), resources.getString(R.string.create_bookmark));
});
// Create a double-tap listener to toggle full-screen mode.
@@ -821,11 +817,18 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
}
});
- // Implement swipe to refresh
- swipeRefreshLayout = findViewById(R.id.swipe_refreshlayout);
- swipeRefreshLayout.setColorSchemeResources(R.color.blue_700);
+ // Implement swipe to refresh.
+ swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
swipeRefreshLayout.setOnRefreshListener(() -> mainWebView.reload());
+ // Set the swipe to refresh color according to the theme.
+ if (darkTheme) {
+ swipeRefreshLayout.setColorSchemeResources(R.color.blue_600);
+ swipeRefreshLayout.setProgressBackgroundColorSchemeResource(R.color.gray_850);
+ } else {
+ swipeRefreshLayout.setColorSchemeResources(R.color.blue_700);
+ }
+
// `DrawerTitle` identifies the `DrawerLayouts` in accessibility mode.
drawerLayout.setDrawerTitle(GravityCompat.START, getString(R.string.navigation_drawer));
drawerLayout.setDrawerTitle(GravityCompat.END, getString(R.string.bookmarks));
@@ -891,17 +894,29 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
// Show the edit bookmark folder `AlertDialog` and name the instance `@string/edit_folder`.
AppCompatDialogFragment editFolderDialog = EditBookmarkFolderDialog.folderDatabaseId(databaseId);
- editFolderDialog.show(getSupportFragmentManager(), getResources().getString(R.string.edit_folder));
+ editFolderDialog.show(getSupportFragmentManager(), resources.getString(R.string.edit_folder));
} else {
// Show the edit bookmark `AlertDialog` and name the instance `@string/edit_bookmark`.
AppCompatDialogFragment editBookmarkDialog = EditBookmarkDialog.bookmarkDatabaseId(databaseId);
- editBookmarkDialog.show(getSupportFragmentManager(), getResources().getString(R.string.edit_bookmark));
+ editBookmarkDialog.show(getSupportFragmentManager(), resources.getString(R.string.edit_bookmark));
}
// Consume the event.
return true;
});
+ // Get the status bar pixel size.
+ int statusBarResourceId = resources.getIdentifier("status_bar_height", "dimen", "android");
+ int statusBarPixelSize = resources.getDimensionPixelSize(statusBarResourceId);
+
+ // Get the resource density.
+ float screenDensity = resources.getDisplayMetrics().density;
+
+ // Calculate the drawer header padding. This is used to move the text in the drawer headers below any cutouts.
+ int drawerHeaderPaddingLeftAndRight = (int) (15 * screenDensity);
+ int drawerHeaderPaddingTop = statusBarPixelSize + (int) (4 * screenDensity);
+ int drawerHeaderPaddingBottom = (int) (8 * screenDensity);
+
// The drawer listener is used to update the navigation menu.
drawerLayout.addDrawerListener(new DrawerLayout.DrawerListener() {
@Override
@@ -919,6 +934,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
@Override
public void onDrawerStateChanged(int newState) {
if ((newState == DrawerLayout.STATE_SETTLING) || (newState == DrawerLayout.STATE_DRAGGING)) { // A drawer is opening or closing.
+ // Get handles for the drawer headers.
+ TextView navigationHeaderTextView = findViewById(R.id.navigationText);
+ TextView bookmarksHeaderTextView = findViewById(R.id.bookmarks_title_textview);
+
+ // Apply the calculated drawer paddings. This moves the text in the header below any cutouts.
+ navigationHeaderTextView.setPadding(drawerHeaderPaddingLeftAndRight, drawerHeaderPaddingTop, drawerHeaderPaddingLeftAndRight, drawerHeaderPaddingBottom);
+ bookmarksHeaderTextView.setPadding(drawerHeaderPaddingLeftAndRight, drawerHeaderPaddingTop, drawerHeaderPaddingLeftAndRight, drawerHeaderPaddingBottom);
+
// Update the back, forward, history, and requests menu items.
navigationBackMenuItem.setEnabled(mainWebView.canGoBack());
navigationForwardMenuItem.setEnabled(mainWebView.canGoForward());
@@ -928,8 +951,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 menus, which otherwise draw above the open drawers.
urlTextBox.clearFocus();
+ mainWebView.clearFocus();
}
}
});
@@ -1147,32 +1171,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));
+ }
}
});
@@ -1200,17 +1229,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
// Initialize the default preference values the first time the program is run. `false` keeps this command from resetting any current preferences back to default.
PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
- // Get the intent that started the app.
- final Intent launchingIntent = getIntent();
-
- // Extract the launching intent data as `launchingIntentUriData`.
- final Uri launchingIntentUriData = launchingIntent.getData();
-
- // Convert the launching intent URI data (if it exists) to a string and store it in `formattedUrlString`.
- if (launchingIntentUriData != null) {
- formattedUrlString = launchingIntentUriData.toString();
- }
-
// Get a handle for the `Runtime`.
privacyBrowserRuntime = Runtime.getRuntime();
@@ -1247,8 +1265,8 @@ 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);
- userAgentDataArray = getResources().getStringArray(R.array.user_agent_data);
+ userAgentNamesArray = ArrayAdapter.createFromResource(this, R.array.user_agent_names, R.layout.spinner_item);
+ userAgentDataArray = resources.getStringArray(R.array.user_agent_data);
// Apply the app settings from the shared preferences.
applyAppSettings();
@@ -1287,10 +1305,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 +1612,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.
@@ -1617,7 +1649,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
public void onPageFinished(WebView view, String url) {
// Reset the wide view port if it has been turned off by the waiting for Orbot message.
if (!waitingForOrbot) {
- mainWebView.getSettings().setUseWideViewPort(true);
+ // Only use a wide view port if the URL starts with `http`, not for `file://` and `content://`.
+ mainWebView.getSettings().setUseWideViewPort(url.startsWith("http"));
}
// Flush any cookies to persistent storage. `CookieManager` has become very lazy about flushing cookies in recent versions.
@@ -1699,7 +1732,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 = "";
@@ -1796,6 +1830,32 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
}
});
+ // Get the intent that started the app.
+ Intent launchingIntent = getIntent();
+
+ // Get the information from the intent.
+ String launchingIntentAction = launchingIntent.getAction();
+ Uri launchingIntentUriData = launchingIntent.getData();
+
+ // If the intent action is a web search, perform the search.
+ if ((launchingIntentAction != null) && launchingIntentAction.equals(Intent.ACTION_WEB_SEARCH)) {
+ // Create an encoded URL string.
+ String encodedUrlString;
+
+ // Sanitize the search input and convert it to a search.
+ try {
+ encodedUrlString = URLEncoder.encode(launchingIntent.getStringExtra(SearchManager.QUERY), "UTF-8");
+ } catch (UnsupportedEncodingException exception) {
+ encodedUrlString = "";
+ }
+
+ // Add the base search URL.
+ formattedUrlString = searchURL + encodedUrlString;
+ } else if (launchingIntentUriData != null){ // Check to see if the intent contains a new URL.
+ // Set the formatted URL string.
+ formattedUrlString = launchingIntentUriData.toString();
+ }
+
// Load the website if not waiting for Orbot to connect.
if (!waitingForOrbot) {
loadUrl(formattedUrlString);
@@ -1807,27 +1867,44 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
// Sets the new intent as the activity intent, so that any future `getIntent()`s pick up this one instead of creating a new activity.
setIntent(intent);
- // Check to see if the intent contains a new URL.
- if (intent.getData() != null) {
- // Get the intent data.
- final Uri intentUriData = intent.getData();
+ // Get the information from the intent.
+ String intentAction = intent.getAction();
+ Uri intentUriData = intent.getData();
- // Load the website.
- loadUrl(intentUriData.toString());
+ // If the intent action is a web search, perform the search.
+ if ((intentAction != null) && intentAction.equals(Intent.ACTION_WEB_SEARCH)) {
+ // Create an encoded URL string.
+ String encodedUrlString;
- // Close the navigation drawer if it is open.
- if (drawerLayout.isDrawerVisible(GravityCompat.START)) {
- drawerLayout.closeDrawer(GravityCompat.START);
+ // Sanitize the search input and convert it to a search.
+ try {
+ encodedUrlString = URLEncoder.encode(intent.getStringExtra(SearchManager.QUERY), "UTF-8");
+ } catch (UnsupportedEncodingException exception) {
+ encodedUrlString = "";
}
- // Close the bookmarks drawer if it is open.
- if (drawerLayout.isDrawerVisible(GravityCompat.END)) {
- drawerLayout.closeDrawer(GravityCompat.END);
- }
+ // Add the base search URL.
+ formattedUrlString = searchURL + encodedUrlString;
+ } else if (intentUriData != null){ // Check to see if the intent contains a new URL.
+ // Set the formatted URL string.
+ formattedUrlString = intentUriData.toString();
+ }
- // Clear the keyboard if displayed and remove the focus on the urlTextBar if it has it.
- mainWebView.requestFocus();
+ // Load the URL.
+ loadUrl(formattedUrlString);
+
+ // Close the navigation drawer if it is open.
+ if (drawerLayout.isDrawerVisible(GravityCompat.START)) {
+ drawerLayout.closeDrawer(GravityCompat.START);
+ }
+
+ // Close the bookmarks drawer if it is open.
+ if (drawerLayout.isDrawerVisible(GravityCompat.END)) {
+ drawerLayout.closeDrawer(GravityCompat.END);
}
+
+ // Clear the keyboard if displayed and remove the focus on the urlTextBar if it has it.
+ mainWebView.requestFocus();
}
@Override
@@ -1923,7 +2000,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
mainWebView.getSettings().setUseWideViewPort(false);
// Load a waiting page. `null` specifies no encoding, which defaults to ASCII.
- mainWebView.loadData(waitingForOrbotHTMLString, "text/html", null);
+ mainWebView.loadData(waitingForOrbotHtmlString, "text/html", null);
}
if (displayingFullScreenVideo) {
@@ -2060,6 +2137,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
MenuItem swipeToRefreshMenuItem = menu.findItem(R.id.swipe_to_refresh);
MenuItem displayImagesMenuItem = menu.findItem(R.id.display_images);
MenuItem nightModeMenuItem = menu.findItem(R.id.night_mode);
+ MenuItem proxyThroughOrbotMenuItem = menu.findItem(R.id.proxy_through_orbot);
// Set the text for the domain menu item.
if (domainSettingsApplied) {
@@ -2082,6 +2160,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
swipeToRefreshMenuItem.setChecked(swipeRefreshLayout.isEnabled());
displayImagesMenuItem.setChecked(mainWebView.getSettings().getLoadsImagesAutomatically());
nightModeMenuItem.setChecked(nightMode);
+ proxyThroughOrbotMenuItem.setChecked(proxyThroughOrbot);
// Enable third-party cookies if first-party cookies are enabled.
toggleThirdPartyCookiesMenuItem.setEnabled(firstPartyCookiesEnabled);
@@ -2723,9 +2802,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:
@@ -2744,7 +2820,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.
@@ -2757,19 +2833,40 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
mainWebView.reload();
return true;
+ case R.id.print:
+ // Get a `PrintManager` instance.
+ PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE);
+
+ // Convert `mainWebView` to `printDocumentAdapter`.
+ PrintDocumentAdapter printDocumentAdapter = mainWebView.createPrintDocumentAdapter();
+
+ // Remove the lint error below that `printManager` might be `null`.
+ assert printManager != null;
+
+ // Print the document. The print attributes are `null`.
+ printManager.print(getString(R.string.privacy_browser_web_page), printDocumentAdapter, null);
+ return true;
+
case R.id.view_source:
// Launch the View Source activity.
Intent viewSourceIntent = new Intent(this, ViewSourceActivity.class);
startActivity(viewSourceIntent);
return true;
+ case R.id.proxy_through_orbot:
+ // Toggle the proxy through Orbot variable.
+ proxyThroughOrbot = !proxyThroughOrbot;
+
+ // Apply the proxy through Orbot settings.
+ applyProxyThroughOrbot(true);
+ return true;
+
case R.id.share:
// Setup the share string.
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");
@@ -2795,20 +2892,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
}, 200);
return true;
- case R.id.print:
- // Get a `PrintManager` instance.
- PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE);
-
- // Convert `mainWebView` to `printDocumentAdapter`.
- PrintDocumentAdapter printDocumentAdapter = mainWebView.createPrintDocumentAdapter();
-
- // Remove the lint error below that `printManager` might be `null`.
- assert printManager != null;
-
- // Print the document. The print attributes are `null`.
- printManager.print(getString(R.string.privacy_browser_web_page), printDocumentAdapter, null);
- return true;
-
case R.id.add_to_homescreen:
// Show the `CreateHomeScreenShortcutDialog` `AlertDialog` and name this instance `R.string.create_shortcut`.
AppCompatDialogFragment createHomeScreenShortcutDialogFragment = new CreateHomeScreenShortcutDialog();
@@ -3135,32 +3218,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;
});
@@ -3178,7 +3264,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`.
@@ -3222,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;
});
@@ -3281,30 +3370,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;
});
@@ -3411,8 +3503,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.
@@ -3792,14 +3883,18 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
}
}
- private void loadUrlFromTextBox() throws UnsupportedEncodingException {
+ private void loadUrlFromTextBox() {
// Get the text from urlTextBox and convert it to a string. trim() removes white spaces from the beginning and end of the string.
String unformattedUrlString = urlTextBox.getText().toString().trim();
// Check to see if `unformattedUrlString` is a valid URL. Otherwise, convert it into a search.
- if ((Patterns.WEB_URL.matcher(unformattedUrlString).matches()) || (unformattedUrlString.startsWith("http://")) || (unformattedUrlString.startsWith("https://"))) {
- // Add `https://` at the beginning if it is missing. Otherwise the app will segfault.
- if (!unformattedUrlString.startsWith("http")) {
+ if (unformattedUrlString.startsWith("content://")) {
+ // Load the entire content URL.
+ formattedUrlString = unformattedUrlString;
+ } else if (Patterns.WEB_URL.matcher(unformattedUrlString).matches() || unformattedUrlString.startsWith("http://") || unformattedUrlString.startsWith("https://")
+ || unformattedUrlString.startsWith("file://")) {
+ // Add `https://` at the beginning if there is no protocol. Otherwise the app will segfault.
+ if (!unformattedUrlString.startsWith("http") && !unformattedUrlString.startsWith("file://") && !unformattedUrlString.startsWith("content://")) {
unformattedUrlString = "https://" + unformattedUrlString;
}
@@ -3814,24 +3909,36 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
}
// The ternary operator (? :) makes sure that a null pointer exception is not thrown, which would happen if `.get` was called on a `null` value.
- final String scheme = unformattedUrl != null ? unformattedUrl.getProtocol() : null;
- final String authority = unformattedUrl != null ? unformattedUrl.getAuthority() : null;
- final String path = unformattedUrl != null ? unformattedUrl.getPath() : null;
- final String query = unformattedUrl != null ? unformattedUrl.getQuery() : null;
- final String fragment = unformattedUrl != null ? unformattedUrl.getRef() : null;
+ String scheme = unformattedUrl != null ? unformattedUrl.getProtocol() : null;
+ String authority = unformattedUrl != null ? unformattedUrl.getAuthority() : null;
+ String path = unformattedUrl != null ? unformattedUrl.getPath() : null;
+ String query = unformattedUrl != null ? unformattedUrl.getQuery() : null;
+ String fragment = unformattedUrl != null ? unformattedUrl.getRef() : null;
// Build the URI.
Uri.Builder formattedUri = new Uri.Builder();
formattedUri.scheme(scheme).authority(authority).path(path).query(query).fragment(fragment);
// Decode `formattedUri` as a `String` in `UTF-8`.
- formattedUrlString = URLDecoder.decode(formattedUri.build().toString(), "UTF-8");
+ try {
+ formattedUrlString = URLDecoder.decode(formattedUri.build().toString(), "UTF-8");
+ } catch (UnsupportedEncodingException exception) {
+ // Load a blank string.
+ formattedUrlString = "";
+ }
} else if (unformattedUrlString.isEmpty()){ // Load a blank web site.
// Load a blank string.
formattedUrlString = "";
} else { // Search for the contents of the URL box.
- // Sanitize the search input and convert it to a search.
- final String encodedUrlString = URLEncoder.encode(unformattedUrlString, "UTF-8");
+ // Create an encoded URL String.
+ String encodedUrlString;
+
+ // Sanitize the search input.
+ try {
+ encodedUrlString = URLEncoder.encode(unformattedUrlString, "UTF-8");
+ } catch (UnsupportedEncodingException exception) {
+ encodedUrlString = "";
+ }
// Add the base search URL.
formattedUrlString = searchURL + encodedUrlString;
@@ -3890,87 +3997,16 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
// Store the values from the shared preferences in variables.
- 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", "");
incognitoModeEnabled = sharedPreferences.getBoolean("incognito_mode", false);
boolean doNotTrackEnabled = sharedPreferences.getBoolean("do_not_track", false);
proxyThroughOrbot = sharedPreferences.getBoolean("proxy_through_orbot", false);
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);
-
- // Set the homepage, search, and proxy options.
- if (proxyThroughOrbot) { // Set the Tor options.
- // Set `torHomepageString` as `homepage`.
- homepage = torHomepageString;
-
- // If formattedUrlString is null assign the homepage to it.
- if (formattedUrlString == null) {
- formattedUrlString = homepage;
- }
-
- // Set the search URL.
- if (torSearchString.equals("Custom URL")) { // Get the custom URL string.
- searchURL = torSearchCustomURLString;
- } else { // Use the string from the pre-built list.
- searchURL = torSearchString;
- }
-
- // Set the proxy. `this` refers to the current activity where an `AlertDialog` might be displayed.
- OrbotProxyHelper.setProxy(getApplicationContext(), this, "localhost", "8118");
-
- // Set the `appBar` background to indicate proxying through Orbot is enabled. `this` refers to the context.
- if (darkTheme) {
- appBar.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.dark_blue_30));
- } else {
- appBar.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.blue_50));
- }
-
- // Display a message to the user if waiting for Orbot.
- if (!orbotStatus.equals("ON")) {
- // Set `waitingForOrbot`.
- waitingForOrbot = true;
-
- // Disable the wide view port so that the waiting for Orbot text is displayed correctly.
- mainWebView.getSettings().setUseWideViewPort(false);
-
- // Load a waiting page. `null` specifies no encoding, which defaults to ASCII.
- mainWebView.loadData(waitingForOrbotHTMLString, "text/html", null);
- }
- } else { // Set the non-Tor options.
- // Set `homepageString` as `homepage`.
- homepage = homepageString;
+ downloadWithExternalApp = sharedPreferences.getBoolean("download_with_external_app", false);
- // If formattedUrlString is null assign the homepage to it.
- if (formattedUrlString == null) {
- formattedUrlString = homepage;
- }
-
- // Set the search URL.
- if (searchString.equals("Custom URL")) { // Get the custom URL string.
- searchURL = searchCustomURLString;
- } else { // Use the string from the pre-built list.
- searchURL = searchString;
- }
-
- // Reset the proxy to default. The host is `""` and the port is `"0"`.
- OrbotProxyHelper.setProxy(getApplicationContext(), this, "", "0");
-
- // Set the default `appBar` background. `this` refers to the context.
- if (darkTheme) {
- appBar.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.gray_900));
- } else {
- appBar.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.gray_100));
- }
-
- // Reset `waitingForOrbot.
- waitingForOrbot = false;
- }
+ // Apply the proxy through Orbot settings.
+ applyProxyThroughOrbot(false);
// Set Do Not Track status.
if (doNotTrackEnabled) {
@@ -4043,7 +4079,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);
@@ -4134,13 +4176,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();
@@ -4163,7 +4206,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));
@@ -4279,25 +4322,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.
@@ -4308,15 +4369,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);
@@ -4384,8 +4445,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));
}
@@ -4393,10 +4460,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);
@@ -4407,26 +4470,96 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
if (reloadWebsite) {
mainWebView.reload();
}
+
+ // Return the user agent changed status.
+ return userAgentChanged;
}
- 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;
+ private void applyProxyThroughOrbot(boolean reloadWebsite) {
+ // Get a handle for the shared preferences.
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
- case DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_ENABLED:
- mainWebView.getSettings().setLoadsImagesAutomatically(true);
- break;
+ // Get the search preferences.
+ 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));
- case DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_DISABLED:
- mainWebView.getSettings().setLoadsImagesAutomatically(false);
- break;
- }
- } else { // Default settings are applied.
- mainWebView.getSettings().setLoadsImagesAutomatically(displayWebpageImagesBoolean);
+ // Set the homepage, search, and proxy options.
+ if (proxyThroughOrbot) { // Set the Tor options.
+ // Set `torHomepageString` as `homepage`.
+ homepage = torHomepageString;
+
+ // If formattedUrlString is null assign the homepage to it.
+ if (formattedUrlString == null) {
+ formattedUrlString = homepage;
+ }
+
+ // Set the search URL.
+ if (torSearchString.equals("Custom URL")) { // Get the custom URL string.
+ searchURL = torSearchCustomUrlString;
+ } else { // Use the string from the pre-built list.
+ searchURL = torSearchString;
+ }
+
+ // Set the proxy. `this` refers to the current activity where an `AlertDialog` might be displayed.
+ OrbotProxyHelper.setProxy(getApplicationContext(), this, "localhost", "8118");
+
+ // Set the `appBar` background to indicate proxying through Orbot is enabled. `this` refers to the context.
+ if (darkTheme) {
+ appBar.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.dark_blue_30));
+ } else {
+ appBar.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.blue_50));
+ }
+
+ // Check to see if Orbot is ready.
+ if (!orbotStatus.equals("ON")) { // Orbot is not ready.
+ // Set `waitingForOrbot`.
+ waitingForOrbot = true;
+
+ // Disable the wide view port so that the waiting for Orbot text is displayed correctly.
+ mainWebView.getSettings().setUseWideViewPort(false);
+
+ // Load a waiting page. `null` specifies no encoding, which defaults to ASCII.
+ mainWebView.loadData(waitingForOrbotHtmlString, "text/html", null);
+ } else if (reloadWebsite) { // Orbot is ready and the website should be reloaded.
+ // Reload the website.
+ mainWebView.reload();
+ }
+ } else { // Set the non-Tor options.
+ // Set `homepageString` as `homepage`.
+ homepage = homepageString;
+
+ // If formattedUrlString is null assign the homepage to it.
+ if (formattedUrlString == null) {
+ formattedUrlString = homepage;
+ }
+
+ // Set the search URL.
+ if (searchString.equals("Custom URL")) { // Get the custom URL string.
+ searchURL = searchCustomUrlString;
+ } else { // Use the string from the pre-built list.
+ searchURL = searchString;
+ }
+
+ // Reset the proxy to default. The host is `""` and the port is `"0"`.
+ OrbotProxyHelper.setProxy(getApplicationContext(), this, "", "0");
+
+ // Set the default `appBar` background. `this` refers to the context.
+ if (darkTheme) {
+ appBar.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.gray_900));
+ } else {
+ appBar.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.gray_100));
+ }
+
+ // Reset `waitingForOrbot.
+ waitingForOrbot = false;
+
+ // Reload the website if requested.
+ if (reloadWebsite) {
+ mainWebView.reload();
}
}
}
@@ -4488,21 +4621,75 @@ 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();
- 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://")) { // De-emphasize the protocol of connections that are encrypted.
- urlTextBox.getText().setSpan(initialGrayColorSpan, 0, 8, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
- }
+ // Highlight the URL according to the protocol.
+ if (urlString.startsWith("file://")) { // This is a file URL.
+ // De-emphasize only the protocol.
+ urlTextBox.getText().setSpan(initialGrayColorSpan, 0, 7, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+ } else if (urlString.startsWith("content://")) {
+ // De-emphasize only the protocol.
+ urlTextBox.getText().setSpan(initialGrayColorSpan, 0, 10, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+ } else { // This is a web URL.
+ // 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 `/` immediately after the domain name.
- int endOfDomainName = urlString.indexOf("/", (urlString.indexOf("//") + 2));
+ // Get the index of the penultimate `.` in the domain.
+ int penultimateDotIndex = baseUrl.lastIndexOf(".", lastDotIndex - 1);
- // De-emphasize the text after the domain name.
- if (endOfDomainName > 0) {
- urlTextBox.getText().setSpan(finalGrayColorSpan, endOfDomainName, urlString.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+ // 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.
+ 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);
+ }
+ }
+
+ // De-emphasize the text after the domain name.
+ if (endOfDomainName > 0) {
+ urlTextBox.getText().setSpan(finalGrayColorSpan, endOfDomainName, urlString.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+ }
}
}