X-Git-Url: https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=blobdiff_plain;f=app%2Fsrc%2Fmain%2Fjava%2Fcom%2Fstoutner%2Fprivacybrowser%2Factivities%2FMainWebView.java;h=3ee406ce8abe77c648230ca9c827766c2e69b0a7;hp=8e06fc8bab2cd24787a7209ccf912c916e7da5e8;hb=acae0260d295ddab13d47bf6e011ae4ebe8b5e44;hpb=d45c5240d1a82d1a43742b6aea8173b520697520 diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebView.java b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebView.java index 8e06fc8b..3ee406ce 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebView.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebView.java @@ -1,5 +1,7 @@ /** - * Copyright 2015-2016 Soren Stoutner . + * Copyright 2015-2017 Soren Stoutner . + * + * Download cookie code contributed 2017 Hendrik Knackstedt. Copyright assigned to Soren Stoutner . * * This file is part of Privacy Browser . * @@ -70,6 +72,7 @@ import android.webkit.DownloadListener; import android.webkit.SslErrorHandler; import android.webkit.WebBackForwardList; import android.webkit.WebChromeClient; +import android.webkit.WebResourceResponse; import android.webkit.WebStorage; import android.webkit.WebView; import android.webkit.WebViewClient; @@ -93,19 +96,25 @@ import com.stoutner.privacybrowser.dialogs.SslCertificateError; import com.stoutner.privacybrowser.dialogs.UrlHistory; import com.stoutner.privacybrowser.dialogs.ViewSslCertificate; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLEncoder; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; // We need to use AppCompatActivity from android.support.v7.app.AppCompatActivity to have access to the SupportActionBar until the minimum API is >= 21. public class MainWebView extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener, CreateHomeScreenShortcut.CreateHomeScreenSchortcutListener, SslCertificateError.SslCertificateErrorListener, DownloadFile.DownloadFileListener, DownloadImage.DownloadImageListener, UrlHistory.UrlHistoryListener { // `appBar` is public static so it can be accessed from `OrbotProxyHelper`. - // It is also used in `onCreate()`, `onOptionsItemSelected()`, and `closeFindOnPage()`. + // It is also used in `onCreate()`, `onOptionsItemSelected()`, `closeFindOnPage()`, and `applySettings()`. public static ActionBar appBar; // `favoriteIcon` is public static so it can be accessed from `CreateHomeScreenShortcut`, `Bookmarks`, `CreateBookmark`, `CreateBookmarkFolder`, and `EditBookmark`. @@ -120,7 +129,6 @@ public class MainWebView extends AppCompatActivity implements NavigationView.OnN public static SslCertificate sslCertificate; - // `drawerLayout` is used in `onCreate()`, `onNewIntent()`, and `onBackPressed()`. private DrawerLayout drawerLayout; @@ -130,16 +138,13 @@ public class MainWebView extends AppCompatActivity implements NavigationView.OnN // 'mainWebView' is used in `onCreate()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onRestart()`, `onCreateContextMenu()`, `findPreviousOnPage()`, `findNextOnPage()`, `closeFindOnPage()`, and `loadUrlFromTextBox()`. private WebView mainWebView; - // `findOnPageEditText` is used in `onCreate()`, `onOptionsItemSelected()`, and `closeFindOnPage()`. - private EditText findOnPageEditText; - // `fullScreenVideoFrameLayout` is used in `onCreate()` and `onConfigurationChanged()`. private FrameLayout fullScreenVideoFrameLayout; // `swipeRefreshLayout` is used in `onCreate()`, `onPrepareOptionsMenu`, and `onRestart()`. private SwipeRefreshLayout swipeRefreshLayout; - // `cookieManager` is used in `onCreate()`, `onOptionsItemSelected()`, and `onNavigationItemSelected()`, and `onRestart()`. + // `cookieManager` is used in `onCreate()`, `onOptionsItemSelected()`, and `onNavigationItemSelected()`, `onDownloadImage()`, `onDownloadFile()`, and `onRestart()`. private CookieManager cookieManager; // `customHeader` is used in `onCreate()`, `onOptionsItemSelected()`, `onCreateContextMenu()`, and `loadUrlFromTextBox()`. @@ -149,7 +154,7 @@ public class MainWebView extends AppCompatActivity implements NavigationView.OnN // It is `Boolean` instead of `boolean` because `applySettings()` needs to know if it is `null`. private Boolean javaScriptEnabled; - // `firstPartyCookiesEnabled` is used in `onCreate()`, `onCreateOptionsMenu()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applySettings()`. + // `firstPartyCookiesEnabled` is used in `onCreate()`, `onCreateOptionsMenu()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, `onDownloadImage()`, `onDownloadFile()`, and `applySettings()`. private boolean firstPartyCookiesEnabled; // `thirdPartyCookiesEnabled` used in `onCreate()`, `onCreateOptionsMenu()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applySettings()`. @@ -173,18 +178,36 @@ public class MainWebView extends AppCompatActivity implements NavigationView.OnN // `javaScriptEnabledSearchURL` is used in `loadURLFromTextBox()` and `applySettings()`. private String javaScriptEnabledSearchURL; + // `adBlockerEnabled` is used in `onCreate()` and `applySettings()`. + private boolean adBlockerEnabled; + + // `fullScreenBrowsingModeEnabled` is used in `onCreate()` and `applySettings()`. + private boolean fullScreenBrowsingModeEnabled; + + // `inFullScreenBrowsingMode` is used in `onCreate()` and `applySettings()`. + private boolean inFullScreenBrowsingMode; + // `hideSystemBarsOnFullscreen` is used in `onCreate()` and `applySettings()`. private boolean hideSystemBarsOnFullscreen; // `translucentNavigationBarOnFullscreen` is used in `onCreate()` and `applySettings()`. private boolean translucentNavigationBarOnFullscreen; + // `findOnPageLinearLayout` is used in `onCreate()`, `onOptionsItemSelected()`, and `closeFindOnPage()`. + private LinearLayout findOnPageLinearLayout; + + // `findOnPageEditText` is used in `onCreate()`, `onOptionsItemSelected()`, and `closeFindOnPage()`. + private EditText findOnPageEditText; + // `mainMenu` is used in `onCreateOptionsMenu()` and `updatePrivacyIcons()`. private Menu mainMenu; // `drawerToggle` is used in `onCreate()`, `onPostCreate()`, `onConfigurationChanged()`, `onNewIntent()`, and `onNavigationItemSelected()`. private ActionBarDrawerToggle drawerToggle; + // `supportAppBar` is used in `onCreate()`, `onOptionsItemSelected()`, and `closeFindOnPage()`. + private Toolbar supportAppBar; + // `urlTextBox` is used in `onCreate()`, `onOptionsItemSelected()`, and `loadUrlFromTextBox()`. private EditText urlTextBox; @@ -197,6 +220,9 @@ public class MainWebView extends AppCompatActivity implements NavigationView.OnN // `inputMethodManager` is used in `onOptionsItemSelected()`, `loadUrlFromTextBox()`, and `closeFindOnPage()`. private InputMethodManager inputMethodManager; + // `mainWebViewRelativeLayout` is used in `onCreate()` and `onNavigationItemSelected()`. + private RelativeLayout mainWebViewRelativeLayout; + @Override // 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. @SuppressLint("SetJavaScriptEnabled") @@ -208,7 +234,7 @@ public class MainWebView extends AppCompatActivity implements NavigationView.OnN inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); // We need to use the `SupportActionBar` from `android.support.v7.app.ActionBar` until the minimum API is >= 21. - Toolbar supportAppBar = (Toolbar) findViewById(R.id.app_bar); + supportAppBar = (Toolbar) findViewById(R.id.app_bar); setSupportActionBar(supportAppBar); appBar = getSupportActionBar(); @@ -244,7 +270,9 @@ public class MainWebView extends AppCompatActivity implements NavigationView.OnN // Get handles for views that need to be accessed. drawerLayout = (DrawerLayout) findViewById(R.id.drawerlayout); rootCoordinatorLayout = (CoordinatorLayout) findViewById(R.id.root_coordinatorlayout); + mainWebViewRelativeLayout = (RelativeLayout) findViewById(R.id.main_webview_relativelayout); mainWebView = (WebView) findViewById(R.id.mainWebView); + findOnPageLinearLayout = (LinearLayout) findViewById(R.id.find_on_page_linearlayout); findOnPageEditText = (EditText) findViewById(R.id.find_on_page_edittext); fullScreenVideoFrameLayout = (FrameLayout) findViewById(R.id.full_screen_video_framelayout); @@ -253,68 +281,75 @@ public class MainWebView extends AppCompatActivity implements NavigationView.OnN // Override `onDoubleTap()`. All other events are handled using the default settings. @Override public boolean onDoubleTap(MotionEvent event) { - if (appBar.isShowing()) { // If `appBar` is visible, switch to full screen mode. - // Hide the `appBar`. - appBar.hide(); + if (fullScreenBrowsingModeEnabled) { // Only process the double-tap if full screen browsing mode is enabled. + // Toggle `inFullScreenBrowsingMode`. + inFullScreenBrowsingMode = !inFullScreenBrowsingMode; - // Hide the `BannerAd` in the free flavor. - if (BuildConfig.FLAVOR.contentEquals("free")) { - BannerAd.hideAd(adView); - } + if (inFullScreenBrowsingMode) { // Switch to full screen mode. + // Hide the `appBar`. + appBar.hide(); - // Modify the system bars. - if (hideSystemBarsOnFullscreen) { // Hide everything. - // Remove the translucent overlays. - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); - - // Remove the translucent status bar overlay on the `Drawer Layout`, which is special and needs its own command. - drawerLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); - - /* SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen. - * SYSTEM_UI_FLAG_HIDE_NAVIGATION hides the navigation bar on the bottom or right of the screen. - * SYSTEM_UI_FLAG_IMMERSIVE_STICKY makes the status and navigation bars translucent and automatically rehides them after they are shown. - */ - rootCoordinatorLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); - - // Set `rootCoordinatorLayout` to fill the whole screen. - rootCoordinatorLayout.setFitsSystemWindows(false); - } else { // Hide everything except the status and navigation bars. - // Set `rootCoordinatorLayout` to fit under the status and navigation bars. - rootCoordinatorLayout.setFitsSystemWindows(false); - - if (translucentNavigationBarOnFullscreen) { // There is an Android Support Library bug that causes a scrim to print on the right side of the `Drawer Layout` when the navigation bar is displayed on the right of the screen. - // Set the navigation bar to be translucent. - getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); + // Hide the `BannerAd` in the free flavor. + if (BuildConfig.FLAVOR.contentEquals("free")) { + BannerAd.hideAd(adView); } - } - } else { // Switch to normal viewing mode. - // Show the `appBar`. - appBar.show(); - // Show the `BannerAd` in the free flavor. - if (BuildConfig.FLAVOR.contentEquals("free")) { - // Reload the ad. Because the screen may have rotated, we need to use `reloadAfterRotate`. - BannerAd.reloadAfterRotate(adView, getApplicationContext(), getString(R.string.ad_id)); + // Modify the system bars. + if (hideSystemBarsOnFullscreen) { // Hide everything. + // Remove the translucent overlays. + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + + // Remove the translucent status bar overlay on the `Drawer Layout`, which is special and needs its own command. + drawerLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); + + /* SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen. + * SYSTEM_UI_FLAG_HIDE_NAVIGATION hides the navigation bar on the bottom or right of the screen. + * SYSTEM_UI_FLAG_IMMERSIVE_STICKY makes the status and navigation bars translucent and automatically rehides them after they are shown. + */ + rootCoordinatorLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); + + // Set `rootCoordinatorLayout` to fill the whole screen. + rootCoordinatorLayout.setFitsSystemWindows(false); + } else { // Hide everything except the status and navigation bars. + // Set `rootCoordinatorLayout` to fit under the status and navigation bars. + rootCoordinatorLayout.setFitsSystemWindows(false); + + if (translucentNavigationBarOnFullscreen) { // There is an Android Support Library bug that causes a scrim to print on the right side of the `Drawer Layout` when the navigation bar is displayed on the right of the screen. + // Set the navigation bar to be translucent. + getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); + } + } + } else { // Switch to normal viewing mode. + // Show the `appBar`. + appBar.show(); - // Reinitialize the `adView` variable, as the `View` will have been removed and re-added by `BannerAd.reloadAfterRotate()`. - adView = findViewById(R.id.adView); - } + // Show the `BannerAd` in the free flavor. + if (BuildConfig.FLAVOR.contentEquals("free")) { + // Reload the ad. Because the screen may have rotated, we need to use `reloadAfterRotate`. + BannerAd.reloadAfterRotate(adView, getApplicationContext(), getString(R.string.ad_id)); - // Remove the translucent navigation bar flag if it is set. - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); + // Reinitialize the `adView` variable, as the `View` will have been removed and re-added by `BannerAd.reloadAfterRotate()`. + adView = findViewById(R.id.adView); + } - // Add the translucent status flag if it is unset. This also resets `drawerLayout's` `View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN`. - getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + // Remove the translucent navigation bar flag if it is set. + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); - // Remove any `SYSTEM_UI` flags from `rootCoordinatorLayout`. - rootCoordinatorLayout.setSystemUiVisibility(0); + // Add the translucent status flag if it is unset. This also resets `drawerLayout's` `View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN`. + getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); - // Constrain `rootCoordinatorLayout` inside the status and navigation bars. - rootCoordinatorLayout.setFitsSystemWindows(true); - } + // Remove any `SYSTEM_UI` flags from `rootCoordinatorLayout`. + rootCoordinatorLayout.setSystemUiVisibility(0); - // Consume the double-tap. - return true; + // Constrain `rootCoordinatorLayout` inside the status and navigation bars. + rootCoordinatorLayout.setFitsSystemWindows(true); + } + + // Consume the double-tap. + return true; + } else { // Do not consume the double-tap because full screen browsing mode is disabled. + return false; + } } }); @@ -435,6 +470,28 @@ public class MainWebView extends AppCompatActivity implements NavigationView.OnN // drawerToggle creates the hamburger icon at the start of the AppBar. drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, supportAppBar, R.string.open_navigation_drawer, R.string.close_navigation_drawer); + // Initialize `adServerSet`. + final Set adServersSet = new HashSet<>(); + + // Load the list of ad servers into memory. + try { + // Load `pgl.yoyo.org_adservers.txt` into a `BufferedReader`. + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(getAssets().open("pgl.yoyo.org_adservers.txt"))); + + // Create a string for storing each ad server. + String adServer; + + // Populate `adServersSet`. + while ((adServer = bufferedReader.readLine()) != null) { + adServersSet.add(adServer); + } + + // Close `bufferedReader`. + bufferedReader.close(); + } catch (IOException ioException) { + // We're pretty sure the asset exists, so we don't need to worry about the `IOException` ever being thrown. + } + mainWebView.setWebViewClient(new WebViewClient() { // `shouldOverrideUrlLoading` makes this `WebView` the default handler for URLs inside the app, so that links are not kicked out to other apps. // We have to use the deprecated `shouldOverrideUrlLoading` until API >= 24. @@ -461,6 +518,43 @@ public class MainWebView extends AppCompatActivity implements NavigationView.OnN } } + // Block ads. We have to use the deprecated `shouldInterceptRequest` until minimum API >= 21. + @SuppressWarnings("deprecation") + @Override + public WebResourceResponse shouldInterceptRequest(WebView view, String url){ + if (adBlockerEnabled) { // Block ads. + // Extract the host from `url`. + Uri requestUri = Uri.parse(url); + String requestHost = requestUri.getHost(); + + // Create a variable to track if this is an ad server. + boolean requestHostIsAdServer = false; + + // Check all the subdomains of `requestHost` if it is not `null`. + if (requestHost != null) { + while (requestHost.contains(".")) { + if (adServersSet.contains(requestHost)) { + requestHostIsAdServer = true; + } + + // Strip out the lowest subdomain of `requestHost`. + requestHost = requestHost.substring(requestHost.indexOf(".") + 1); + } + } + + if (requestHostIsAdServer) { // It is an ad server. + // Return an empty `WebResourceResponse`. + return new WebResourceResponse("text/plain", "utf8", new ByteArrayInputStream("".getBytes())); + } else { // It is not an ad server. + // `return null` loads the requested resource. + return null; + } + } else { // Ad blocking is disabled. + // `return null` loads the requested resource. + return null; + } + } + // Update the URL in urlTextBox when the page starts to load. @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { @@ -600,7 +694,7 @@ public class MainWebView extends AppCompatActivity implements NavigationView.OnN // Replace the header that `WebView` creates for `X-Requested-With` with a null value. The default value is the application ID (com.stoutner.privacybrowser.standard). customHeaders.put("X-Requested-With", ""); - // Initialize the default preference values the first time the program is run. + // Initialize the default preference values the first time the program is run. `this` is the context. `false` keeps this command from resetting any current preferences back to default. PreferenceManager.setDefaultValues(this, R.xml.preferences, false); // Apply the settings from the shared preferences. @@ -631,6 +725,9 @@ public class MainWebView extends AppCompatActivity implements NavigationView.OnN favoriteIcon = favoriteIconBitmapDrawable.getBitmap(); } + // Initialize `inFullScreenBrowsingMode`, which is always false at this point because Privacy Browser never starts in full screen browsing mode. + inFullScreenBrowsingMode = false; + // Initialize AdView for the free flavor and request an ad. If this is not the free flavor BannerAd.requestAd() does nothing. adView = findViewById(R.id.adView); BannerAd.requestAd(adView); @@ -971,11 +1068,9 @@ public class MainWebView extends AppCompatActivity implements NavigationView.OnN case R.id.find_on_page: // Hide the URL app bar. - Toolbar appBarToolbar = (Toolbar) findViewById(R.id.app_bar); - appBarToolbar.setVisibility(View.GONE); + supportAppBar.setVisibility(View.GONE); // Show the Find on Page `RelativeLayout`. - LinearLayout findOnPageLinearLayout = (LinearLayout) findViewById(R.id.find_on_page_linearlayout); findOnPageLinearLayout.setVisibility(View.VISIBLE); // Display the keyboard. We have to wait 200 ms before running the command to work around a bug in Android. @@ -1085,6 +1180,14 @@ public class MainWebView extends AppCompatActivity implements NavigationView.OnN startActivity(settingsIntent); break; + /* + case R.id.domains: + // Launch `DomainsList`. + Intent domainsIntent = new Intent(this, DomainsList.class); + startActivity(domainsIntent); + break; + */ + case R.id.guide: // Launch `Guide`. Intent guideIntent = new Intent(this, Guide.class); @@ -1129,12 +1232,20 @@ public class MainWebView extends AppCompatActivity implements NavigationView.OnN customHeaders.clear(); // Detach all views from `mainWebViewRelativeLayout`. - RelativeLayout mainWebViewRelativeLayout = (RelativeLayout) findViewById(R.id.mainWebViewRelativeLayout); mainWebViewRelativeLayout.removeAllViews(); // Destroy the internal state of `mainWebView`. mainWebView.destroy(); + // Manually delete the `app_webview` folder, which contains an additional `WebView` cache. See `https://code.google.com/p/android/issues/detail?id=233826&thanks=233826&ts=1486670530`. + Runtime runtime = Runtime.getRuntime(); + String dataDirString = getApplicationInfo().dataDir; // `dataDir` will vary, but will be something like `/data/user/0/com.stoutner.privacybrowser.standard`, which links to `/data/data/com.stoutner.privacybrowser.standard`. + try { + runtime.exec("rm -rf " + dataDirString + "/app_webview"); + } catch (IOException e) { + // Do nothing if the files do not exist. + } + // Close Privacy Browser. `finishAndRemoveTask` also removes Privacy Browser from the recent app list. if (Build.VERSION.SDK_INT >= 21) { finishAndRemoveTask(); @@ -1384,66 +1495,95 @@ public class MainWebView extends AppCompatActivity implements NavigationView.OnN @Override public void onDownloadImage(AppCompatDialogFragment dialogFragment, String imageUrl) { - // Get a handle for the system `DOWNLOAD_SERVICE`. - DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE); + // Download the image if it has an HTTP or HTTPS URI. + if (imageUrl.startsWith("http")) { + // Get a handle for the system `DOWNLOAD_SERVICE`. + DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE); - // Parse `imageUrl`. - DownloadManager.Request downloadRequest = new DownloadManager.Request(Uri.parse(imageUrl)); + // Parse `imageUrl`. + DownloadManager.Request downloadRequest = new DownloadManager.Request(Uri.parse(imageUrl)); - // Get the file name from `dialogFragment`. - EditText downloadImageNameEditText = (EditText) dialogFragment.getDialog().findViewById(R.id.download_image_name); - String imageName = downloadImageNameEditText.getText().toString(); + // Pass cookies to download manager if cookies are enabled. This is required to download images from websites that require a login. + if (firstPartyCookiesEnabled) { + // Get the cookies for `imageUrl`. + String cookies = cookieManager.getCookie(imageUrl); - // Once we have `WRITE_EXTERNAL_STORAGE` permissions we can use `setDestinationInExternalPublicDir`. - if (Build.VERSION.SDK_INT >= 23) { // If API >= 23, set the download save in the the `DIRECTORY_DOWNLOADS` using `imageName`. - downloadRequest.setDestinationInExternalFilesDir(this, "/", imageName); - } else { // Only set the title using `imageName`. - downloadRequest.setTitle(imageName); - } + // Add the cookies to `downloadRequest`. In the HTTP request header, cookies are named `Cookie`. + downloadRequest.addRequestHeader("Cookie", cookies); + } + + // Get the file name from `dialogFragment`. + EditText downloadImageNameEditText = (EditText) dialogFragment.getDialog().findViewById(R.id.download_image_name); + String imageName = downloadImageNameEditText.getText().toString(); + + // Once we have `WRITE_EXTERNAL_STORAGE` permissions we can use `setDestinationInExternalPublicDir`. + if (Build.VERSION.SDK_INT >= 23) { // If API >= 23, set the download save in the the `DIRECTORY_DOWNLOADS` using `imageName`. + downloadRequest.setDestinationInExternalFilesDir(this, "/", imageName); + } else { // Only set the title using `imageName`. + downloadRequest.setTitle(imageName); + } - // Allow `MediaScanner` to index the download if it is a media file. - downloadRequest.allowScanningByMediaScanner(); + // Allow `MediaScanner` to index the download if it is a media file. + downloadRequest.allowScanningByMediaScanner(); - // Add the URL as the description for the download. - downloadRequest.setDescription(imageUrl); + // Add the URL as the description for the download. + downloadRequest.setDescription(imageUrl); - // Show the download notification after the download is completed. - downloadRequest.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); + // Show the download notification after the download is completed. + downloadRequest.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); - // Initiate the download. - downloadManager.enqueue(downloadRequest); + // Initiate the download. + downloadManager.enqueue(downloadRequest); + } else { // The image is not an HTTP or HTTPS URI. + Snackbar.make(mainWebView, R.string.cannot_download_image, Snackbar.LENGTH_INDEFINITE).show(); + } } @Override public void onDownloadFile(AppCompatDialogFragment dialogFragment, String downloadUrl) { - // Get a handle for the system `DOWNLOAD_SERVICE`. - DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE); + // Download the file if it has an HTTP or HTTPS URI. + if (downloadUrl.startsWith("http")) { - // Parse `downloadUrl`. - DownloadManager.Request downloadRequest = new DownloadManager.Request(Uri.parse(downloadUrl)); + // Get a handle for the system `DOWNLOAD_SERVICE`. + DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE); - // Get the file name from `dialogFragment`. - EditText downloadFileNameEditText = (EditText) dialogFragment.getDialog().findViewById(R.id.download_file_name); - String fileName = downloadFileNameEditText.getText().toString(); + // Parse `downloadUrl`. + DownloadManager.Request downloadRequest = new DownloadManager.Request(Uri.parse(downloadUrl)); - // Once we have `WRITE_EXTERNAL_STORAGE` permissions we can use `setDestinationInExternalPublicDir`. - if (Build.VERSION.SDK_INT >= 23) { // If API >= 23, set the download save in the the `DIRECTORY_DOWNLOADS` using `fileName`. - downloadRequest.setDestinationInExternalFilesDir(this, "/", fileName); - } else { // Only set the title using `fileName`. - downloadRequest.setTitle(fileName); - } + // Pass cookies to download manager if cookies are enabled. This is required to download files from websites that require a login. + if (firstPartyCookiesEnabled) { + // Get the cookies for `downloadUrl`. + String cookies = cookieManager.getCookie(downloadUrl); + + // Add the cookies to `downloadRequest`. In the HTTP request header, cookies are named `Cookie`. + downloadRequest.addRequestHeader("Cookie", cookies); + } + + // Get the file name from `dialogFragment`. + EditText downloadFileNameEditText = (EditText) dialogFragment.getDialog().findViewById(R.id.download_file_name); + String fileName = downloadFileNameEditText.getText().toString(); - // Allow `MediaScanner` to index the download if it is a media file. - downloadRequest.allowScanningByMediaScanner(); + // Once we have `WRITE_EXTERNAL_STORAGE` permissions we can use `setDestinationInExternalPublicDir`. + if (Build.VERSION.SDK_INT >= 23) { // If API >= 23, set the download location to `/sdcard/Android/data/com.stoutner.privacybrowser.standard/files` named `fileName`. + downloadRequest.setDestinationInExternalFilesDir(this, "/", fileName); + } else { // Only set the title using `fileName`. + downloadRequest.setTitle(fileName); + } + + // Allow `MediaScanner` to index the download if it is a media file. + downloadRequest.allowScanningByMediaScanner(); - // Add the URL as the description for the download. - downloadRequest.setDescription(downloadUrl); + // Add the URL as the description for the download. + downloadRequest.setDescription(downloadUrl); - // Show the download notification after the download is completed. - downloadRequest.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); + // Show the download notification after the download is completed. + downloadRequest.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); - // Initiate the download. - downloadManager.enqueue(downloadRequest); + // Initiate the download. + downloadManager.enqueue(downloadRequest); + } else { // The download is not an HTTP or HTTPS URI. + Snackbar.make(mainWebView, R.string.cannot_download_file, Snackbar.LENGTH_INDEFINITE).show(); + } } public void viewSslCertificate(View view) { @@ -1601,12 +1741,10 @@ public class MainWebView extends AppCompatActivity implements NavigationView.OnN mainWebView.clearMatches(); // Hide the Find on Page `RelativeLayout`. - LinearLayout findOnPageLinearLayout = (LinearLayout) findViewById(R.id.find_on_page_linearlayout); findOnPageLinearLayout.setVisibility(View.GONE); // Show the URL app bar. - Toolbar appBarToolbar = (Toolbar) findViewById(R.id.app_bar); - appBarToolbar.setVisibility(View.VISIBLE); + supportAppBar.setVisibility(View.VISIBLE); // Hide the keyboard so we can see the webpage. `0` indicates no additional flags. inputMethodManager.hideSoftInputFromWindow(mainWebView.getWindowToken(), 0); @@ -1620,14 +1758,21 @@ public class MainWebView extends AppCompatActivity implements NavigationView.OnN String userAgentString = sharedPreferences.getString("user_agent", "PrivacyBrowser/1.0"); String customUserAgentString = sharedPreferences.getString("custom_user_agent", "PrivacyBrowser/1.0"); String javaScriptDisabledSearchString = sharedPreferences.getString("javascript_disabled_search", "https://duckduckgo.com/html/?q="); - String javaScriptDisabledCustomSearchString = sharedPreferences.getString("javascript_disabled_search_custom_url", ""); + String javaScriptDisabledSearchCustomURLString = sharedPreferences.getString("javascript_disabled_search_custom_url", ""); String javaScriptEnabledSearchString = sharedPreferences.getString("javascript_enabled_search", "https://duckduckgo.com/?q="); - String javaScriptEnabledCustomSearchString = sharedPreferences.getString("javascript_enabled_search_custom_url", ""); + String javaScriptEnabledSearchCustomURLString = sharedPreferences.getString("javascript_enabled_search_custom_url", ""); String homepageString = sharedPreferences.getString("homepage", "https://www.duckduckgo.com"); + String torHomepageString = sharedPreferences.getString("tor_homepage", "https://3g2upl4pq6kufc4m.onion"); + String torJavaScriptDisabledSearchString = sharedPreferences.getString("tor_javascript_disabled_search", "https://3g2upl4pq6kufc4m.onion/html/?q="); + String torJavaScriptDisabledSearchCustomURLString = sharedPreferences.getString("tor_javascript_disabled_search_custom_url", ""); + String torJavaScriptEnabledSearchString = sharedPreferences.getString("tor_javascript_enabled_search", "https://3g2upl4pq6kufc4m.onion/?q="); + String torJavaScriptEnabledSearchCustomURLString = sharedPreferences.getString("tor_javascript_enabled_search_custom_url", ""); String defaultFontSizeString = sharedPreferences.getString("default_font_size", "100"); swipeToRefreshEnabled = sharedPreferences.getBoolean("swipe_to_refresh_enabled", false); - boolean doNotTrackEnabled = sharedPreferences.getBoolean("do_not_track", true); + adBlockerEnabled = sharedPreferences.getBoolean("block_ads", true); + boolean doNotTrackEnabled = sharedPreferences.getBoolean("do_not_track", false); boolean proxyThroughOrbot = sharedPreferences.getBoolean("proxy_through_orbot", false); + fullScreenBrowsingModeEnabled = sharedPreferences.getBoolean("enable_full_screen_browsing_mode", false); hideSystemBarsOnFullscreen = sharedPreferences.getBoolean("hide_system_bars", false); translucentNavigationBarOnFullscreen = sharedPreferences.getBoolean("translucent_navigation_bar", true); @@ -1653,8 +1798,50 @@ public class MainWebView extends AppCompatActivity implements NavigationView.OnN } } - // Apply the other settings from `sharedPreferences`. - homepage = homepageString; + // Set the homepage, search, and proxy options. + if (proxyThroughOrbot) { // Set the Tor options. + // Set `torHomepageString` as `homepage`. + homepage = torHomepageString; + + // Set JavaScript disabled search. + if (torJavaScriptDisabledSearchString.equals("Custom URL")) { // Get the custom URL string. + javaScriptDisabledSearchURL = torJavaScriptDisabledSearchCustomURLString; + } else { // Use the string from the pre-built list. + javaScriptDisabledSearchURL = torJavaScriptDisabledSearchString; + } + + // Set JavaScript enabled search. + if (torJavaScriptEnabledSearchString.equals("Custom URL")) { // Get the custom URL string. + javaScriptEnabledSearchURL = torJavaScriptEnabledSearchCustomURLString; + } else { // Use the string from the pre-built list. + javaScriptEnabledSearchURL = torJavaScriptEnabledSearchString; + } + + // Set the proxy. `this` refers to the current activity where an `AlertDialog` might be displayed. + OrbotProxyHelper.setProxy(getApplicationContext(), this, "localhost", "8118"); + } else { // Set the non-Tor options. + // Set `homepageString` as `homepage`. + homepage = homepageString; + + // Set JavaScript disabled search. + if (javaScriptDisabledSearchString.equals("Custom URL")) { // Get the custom URL string. + javaScriptDisabledSearchURL = javaScriptDisabledSearchCustomURLString; + } else { // Use the string from the pre-built list. + javaScriptDisabledSearchURL = javaScriptDisabledSearchString; + } + + // Set JavaScript enabled search. + if (javaScriptEnabledSearchString.equals("Custom URL")) { // Get the custom URL string. + javaScriptEnabledSearchURL = javaScriptEnabledSearchCustomURLString; + } else { // Use the string from the pre-built list. + javaScriptEnabledSearchURL = javaScriptEnabledSearchString; + } + + // Reset the proxy to default. The host is `""` and the port is `"0"`. + OrbotProxyHelper.setProxy(getApplicationContext(), this, "", "0"); + } + + // Set swipe to refresh. swipeRefreshLayout.setEnabled(swipeToRefreshEnabled); // Set the user agent initial status. @@ -1675,20 +1862,6 @@ public class MainWebView extends AppCompatActivity implements NavigationView.OnN break; } - // Set JavaScript disabled search. - if (javaScriptDisabledSearchString.equals("Custom URL")) { // Get the custom URL string. - javaScriptDisabledSearchURL = javaScriptDisabledCustomSearchString; - } else { // Use the string from the pre-built list. - javaScriptDisabledSearchURL = javaScriptDisabledSearchString; - } - - // Set JavaScript enabled search. - if (javaScriptEnabledSearchString.equals("Custom URL")) { // Get the custom URL string. - javaScriptEnabledSearchURL = javaScriptEnabledCustomSearchString; - } else { // Use the string from the pre-built list. - javaScriptEnabledSearchURL = javaScriptEnabledSearchString; - } - // Set Do Not Track status. if (doNotTrackEnabled) { customHeaders.put("DNT", "1"); @@ -1696,16 +1869,8 @@ public class MainWebView extends AppCompatActivity implements NavigationView.OnN customHeaders.remove("DNT"); } - // Set Orbot proxy status. - if (proxyThroughOrbot) { - // Set the proxy. `this` refers to the current activity where an `AlertDialog` might be displayed. - OrbotProxyHelper.setProxy(getApplicationContext(), this, "localhost", "8118"); - } else { // Reset the proxy to default. The host is `""` and the port is `"0"`. - OrbotProxyHelper.setProxy(getApplicationContext(), this, "", "0"); - } - // If we are in full screen mode update the `SYSTEM_UI` flags. - if (!appBar.isShowing()) { + if (inFullScreenBrowsingMode) { if (hideSystemBarsOnFullscreen) { // Hide everything. // Remove the translucent navigation setting if it is currently flagged. getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);