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;
import android.text.Spanned;
import android.text.TextWatcher;
import android.text.style.ForegroundColorSpan;
-import android.util.Log;
import android.util.Patterns;
import android.view.ContextMenu;
import android.view.GestureDetector;
// 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.
// 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);
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);
// 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.
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.
// 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
@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());
// Initialize the user agent array adapter and string array.
userAgentNamesArray = ArrayAdapter.createFromResource(this, R.array.user_agent_names, R.layout.spinner_item);
- userAgentDataArray = getResources().getStringArray(R.array.user_agent_data);
+ userAgentDataArray = resources.getStringArray(R.array.user_agent_data);
// Apply the app settings from the shared preferences.
applyAppSettings();
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.
// Create an encoded URL string.
String encodedUrlString;
- Log.i("Intent", launchingIntent.getStringExtra(SearchManager.QUERY));
-
// Sanitize the search input and convert it to a search.
try {
encodedUrlString = URLEncoder.encode(launchingIntent.getStringExtra(SearchManager.QUERY), "UTF-8");
// 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);
- Log.i("Intent", intent.getAction());
-
// Get the information from the intent.
String intentAction = intent.getAction();
Uri intentUriData = intent.getData();
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;
}
// 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));
+ // 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;
+ // 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;
- }
+ 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 last `.` in the domain.
+ int lastDotIndex = baseUrl.lastIndexOf(".");
- // Get the index of the penultimate `.` in the domain.
- int penultimateDotIndex = baseUrl.lastIndexOf(".", lastDotIndex - 1);
+ // 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);
+ // 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 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);
+ // De-emphasize the text after the domain name.
+ if (endOfDomainName > 0) {
+ urlTextBox.getText().setSpan(finalGrayColorSpan, endOfDomainName, urlString.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+ }
}
}