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=ec3a24dd19d691accacee5188c3749fcaaba0785;hb=6a85cc5ca039054a24c4601de785e0bc1a234722;hpb=ac56c9d4b45d50bc161ec07c4d6b0760e6611206 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 ec3a24dd..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; @@ -505,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. @@ -533,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); @@ -549,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); @@ -577,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 { @@ -637,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. @@ -664,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. @@ -892,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 @@ -920,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()); @@ -1207,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(); @@ -1255,7 +1266,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.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(); @@ -1638,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. @@ -1818,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); @@ -1829,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(); + } + + // Load the URL. + loadUrl(formattedUrlString); - // Clear the keyboard if displayed and remove the focus on the urlTextBar if it has it. - mainWebView.requestFocus(); + // 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 @@ -3828,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; } @@ -3850,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; @@ -4568,48 +4639,57 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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); + } } }