import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
import android.graphics.Typeface;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
// Remove the warning about needing to override `performClick()` when using an `OnTouchListener` with `WebView`.
@SuppressLint("ClickableViewAccessibility")
protected void onCreate(Bundle savedInstanceState) {
+ if (Build.VERSION.SDK_INT >= 21) {
+ WebView.enableSlowWholeDocumentDraw();
+ }
+
// 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);
// Set the content view.
setContentView(R.layout.main_framelayout);
+
// Get handles for the views that need to be modified.
DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
Toolbar toolbar = findViewById(R.id.toolbar);
@Override
protected void onNewIntent(Intent intent) {
- // Get the information from the intent.
- String intentAction = intent.getAction();
- Uri intentUriData = intent.getData();
+ // Replace the intent that started the app with this one.
+ setIntent(intent);
- // Determine if this is a web search.
- boolean isWebSearch = ((intentAction != null) && intentAction.equals(Intent.ACTION_WEB_SEARCH));
+ // Process the intent here if Privacy Browser is fully initialized. If the process has been killed by the system while sitting in the background, this will be handled in `initializeWebView()`.
+ if (ultraPrivacy != null) {
+ // Get the information from the intent.
+ String intentAction = intent.getAction();
+ Uri intentUriData = intent.getData();
- // Only process the URI if it contains data or it is a web search. If the user pressed the desktop icon after the app was already running the URI will be null.
- if (intentUriData != null || isWebSearch) {
- // Get the shared preferences.
- SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+ // Determine if this is a web search.
+ boolean isWebSearch = ((intentAction != null) && intentAction.equals(Intent.ACTION_WEB_SEARCH));
- // Create a URL string.
- String url;
+ // Only process the URI if it contains data or it is a web search. If the user pressed the desktop icon after the app was already running the URI will be null.
+ if (intentUriData != null || isWebSearch) {
+ // Get the shared preferences.
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
- // If the intent action is a web search, perform the search.
- if (isWebSearch) {
- // Create an encoded URL string.
- String encodedUrlString;
+ // Create a URL string.
+ String url;
- // Sanitize the search input and convert it to a search.
- try {
- encodedUrlString = URLEncoder.encode(intent.getStringExtra(SearchManager.QUERY), "UTF-8");
- } catch (UnsupportedEncodingException exception) {
- encodedUrlString = "";
- }
+ // If the intent action is a web search, perform the search.
+ if (isWebSearch) {
+ // Create an encoded URL string.
+ String encodedUrlString;
- // Add the base search URL.
- url = searchURL + encodedUrlString;
- } else { // The intent should contain a URL.
- // Set the intent data as the URL.
- url = intentUriData.toString();
- }
+ // Sanitize the search input and convert it to a search.
+ try {
+ encodedUrlString = URLEncoder.encode(intent.getStringExtra(SearchManager.QUERY), "UTF-8");
+ } catch (UnsupportedEncodingException exception) {
+ encodedUrlString = "";
+ }
- // Add a new tab if specified in the preferences.
- if (sharedPreferences.getBoolean("open_intents_in_new_tab", true)) { // Load the URL in a new tab.
- // Set the loading new intent flag.
- loadingNewIntent = true;
+ // Add the base search URL.
+ url = searchURL + encodedUrlString;
+ } else { // The intent should contain a URL.
+ // Set the intent data as the URL.
+ url = intentUriData.toString();
+ }
- // Add a new tab.
- addNewTab(url);
- } else { // Load the URL in the current tab.
- // Make it so.
- loadUrl(url);
- }
+ // Add a new tab if specified in the preferences.
+ if (sharedPreferences.getBoolean("open_intents_in_new_tab", true)) { // Load the URL in a new tab.
+ // Set the loading new intent flag.
+ loadingNewIntent = true;
- // Get a handle for the drawer layout.
- DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
+ // Add a new tab.
+ addNewTab(url);
+ } else { // Load the URL in the current tab.
+ // Make it so.
+ loadUrl(url);
+ }
- // Close the navigation drawer if it is open.
- if (drawerLayout.isDrawerVisible(GravityCompat.START)) {
- drawerLayout.closeDrawer(GravityCompat.START);
- }
+ // Get a handle for the drawer layout.
+ DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
- // Close the bookmarks drawer if it is open.
- if (drawerLayout.isDrawerVisible(GravityCompat.END)) {
- drawerLayout.closeDrawer(GravityCompat.END);
+ // 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);
+ }
}
}
}
printManager.print(getString(R.string.privacy_browser_web_page), printDocumentAdapter, null);
return true;
+ case R.id.save_as_image:
+ // Create a webpage bitmap. Once the Minimum API >= 26 Bitmap.Config.RBGA_F16 can be used instead of ARGB_8888.
+ Bitmap webpageBitmap = Bitmap.createBitmap(currentWebView.getHorizontalScrollRange(), currentWebView.getVerticalScrollRange(), Bitmap.Config.ARGB_8888);
+
+ // Create a canvas.
+ Canvas webpageCanvas = new Canvas(webpageBitmap);
+
+ // Draw the current webpage onto the bitmap.
+ currentWebView.draw(webpageCanvas);
+
+ // Create a webpage PNG byte array output stream.
+ ByteArrayOutputStream webpageByteArrayOutputStream = new ByteArrayOutputStream();
+
+ // Convert the bitmap to a PNG. `0` is for lossless compression (the only option for a PNG).
+ webpageBitmap.compress(Bitmap.CompressFormat.PNG, 0, webpageByteArrayOutputStream);
+
+ // Get a file for the image.
+ File imageFile = new File("/storage/emulated/0/webpage.png");
+
+ // Delete the current file if it exists.
+ if (imageFile.exists()) {
+ //noinspection ResultOfMethodCallIgnored
+ imageFile.delete();
+ }
+
+ try {
+ // Create an image file output stream.
+ FileOutputStream imageFileOutputStream = new FileOutputStream(imageFile);
+
+ // Write the webpage image to the image file.
+ webpageByteArrayOutputStream.writeTo(imageFileOutputStream);
+ } catch (Exception exception) {
+ // Add a snackbar.
+ }
+ return true;
+
case R.id.add_to_homescreen:
// Instantiate the create home screen shortcut dialog.
DialogFragment createHomeScreenShortcutDialogFragment = CreateHomeScreenShortcutDialog.createDialog(currentWebView.getTitle(), currentWebView.getUrl(),
// Check requests against the block lists. The deprecated `shouldInterceptRequest()` must be used until minimum API >= 21.
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
+ // Wait until the blocklists have been populated. When Privacy Browser is being resumed after having the process killed in the background it will try to load the URLs immediately.
+ while (ultraPrivacy == null) {
+ // The wait must be synchronized, which only lets one thread run on it at a time, or `java.lang.IllegalMonitorStateException` is thrown.
+ synchronized (this) {
+ try {
+ // Check to see if the blocklists have been populated after 100 ms.
+ wait(100);
+ } catch (InterruptedException exception) {
+ // Do nothing.
+ }
+ }
+ }
+
// Sanitize the URL.
url = sanitizeUrl(url);