/*
- * Copyright © 2015-2019 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2015-2020 Soren Stoutner <soren@stoutner.com>.
*
* Download cookie code contributed 2017 Hendrik Knackstedt. Copyright assigned to Soren Stoutner <soren@stoutner.com>.
*
import android.net.http.SslError;
import android.os.Build;
import android.os.Bundle;
-import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.fragment.app.DialogFragment;
-import androidx.fragment.app.FragmentManager;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import androidx.viewpager.widget.ViewPager;
import com.stoutner.privacybrowser.adapters.WebViewPagerAdapter;
import com.stoutner.privacybrowser.asynctasks.GetHostIpAddresses;
import com.stoutner.privacybrowser.asynctasks.PopulateBlocklists;
+import com.stoutner.privacybrowser.asynctasks.SaveUrl;
import com.stoutner.privacybrowser.asynctasks.SaveWebpageImage;
import com.stoutner.privacybrowser.dialogs.AdConsentDialog;
import com.stoutner.privacybrowser.dialogs.CreateBookmarkDialog;
import com.stoutner.privacybrowser.dialogs.CreateBookmarkFolderDialog;
import com.stoutner.privacybrowser.dialogs.CreateHomeScreenShortcutDialog;
-import com.stoutner.privacybrowser.dialogs.DownloadFileDialog;
-import com.stoutner.privacybrowser.dialogs.DownloadImageDialog;
-import com.stoutner.privacybrowser.dialogs.DownloadLocationPermissionDialog;
import com.stoutner.privacybrowser.dialogs.EditBookmarkDialog;
import com.stoutner.privacybrowser.dialogs.EditBookmarkFolderDialog;
+import com.stoutner.privacybrowser.dialogs.FontSizeDialog;
import com.stoutner.privacybrowser.dialogs.HttpAuthenticationDialog;
+import com.stoutner.privacybrowser.dialogs.OpenDialog;
+import com.stoutner.privacybrowser.dialogs.ProxyNotInstalledDialog;
import com.stoutner.privacybrowser.dialogs.PinnedMismatchDialog;
-import com.stoutner.privacybrowser.dialogs.SaveWebpageImageDialog;
+import com.stoutner.privacybrowser.dialogs.SaveDialog;
import com.stoutner.privacybrowser.dialogs.SslCertificateErrorDialog;
import com.stoutner.privacybrowser.dialogs.StoragePermissionDialog;
import com.stoutner.privacybrowser.dialogs.UrlHistoryDialog;
import com.stoutner.privacybrowser.dialogs.ViewSslCertificateDialog;
+import com.stoutner.privacybrowser.dialogs.WaitingForProxyDialog;
import com.stoutner.privacybrowser.fragments.WebViewTabFragment;
import com.stoutner.privacybrowser.helpers.AdHelper;
import com.stoutner.privacybrowser.helpers.BlocklistHelper;
import com.stoutner.privacybrowser.helpers.CheckPinnedMismatchHelper;
import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper;
import com.stoutner.privacybrowser.helpers.FileNameHelper;
-import com.stoutner.privacybrowser.helpers.OrbotProxyHelper;
+import com.stoutner.privacybrowser.helpers.ProxyHelper;
import com.stoutner.privacybrowser.views.NestedScrollWebView;
import java.io.ByteArrayInputStream;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
-// AppCompatActivity from android.support.v7.app.AppCompatActivity must be used to have access to the SupportActionBar until the minimum API is >= 21.
public class MainWebViewActivity extends AppCompatActivity implements CreateBookmarkDialog.CreateBookmarkListener, CreateBookmarkFolderDialog.CreateBookmarkFolderListener,
- DownloadFileDialog.DownloadFileListener, DownloadImageDialog.DownloadImageListener, DownloadLocationPermissionDialog.DownloadLocationPermissionDialogListener, EditBookmarkDialog.EditBookmarkListener,
- EditBookmarkFolderDialog.EditBookmarkFolderListener, NavigationView.OnNavigationItemSelectedListener, PinnedMismatchDialog.PinnedMismatchListener, PopulateBlocklists.PopulateBlocklistsListener, SaveWebpageImageDialog.SaveWebpageImageListener,
+ EditBookmarkDialog.EditBookmarkListener, EditBookmarkFolderDialog.EditBookmarkFolderListener, FontSizeDialog.UpdateFontSizeListener, NavigationView.OnNavigationItemSelectedListener,
+ OpenDialog.OpenListener, PinnedMismatchDialog.PinnedMismatchListener, PopulateBlocklists.PopulateBlocklistsListener, SaveDialog.SaveWebpageListener,
StoragePermissionDialog.StoragePermissionDialogListener, UrlHistoryDialog.NavigateHistoryListener, WebViewTabFragment.NewTabListener {
- // `orbotStatus` is public static so it can be accessed from `OrbotProxyHelper`. It is also used in `onCreate()`, `onResume()`, and `applyProxyThroughOrbot()`.
- public static String orbotStatus;
+ // `orbotStatus` is public static so it can be accessed from `OrbotProxyHelper`. It is also used in `onCreate()`, `onResume()`, and `applyProxy()`.
+ public static String orbotStatus = "unknown";
// The WebView pager adapter is accessed from `HttpAuthenticationDialog`, `PinnedMismatchDialog`, and `SslCertificateErrorDialog`. It is also used in `onCreate()`, `onResume()`, and `addTab()`.
public static WebViewPagerAdapter webViewPagerAdapter;
public final static int DOMAINS_WEBVIEW_DEFAULT_USER_AGENT = 2;
public final static int DOMAINS_CUSTOM_USER_AGENT = 13;
- // Start activity for result request codes.
- private final int FILE_UPLOAD_REQUEST_CODE = 0;
- public final static int BROWSE_SAVE_WEBPAGE_IMAGE_REQUEST_CODE = 1;
+ // Start activity for result request codes. The public static entries are accessed from `OpenDialog()` and `SaveWebpageDialog()`.
+ public final static int BROWSE_OPEN_REQUEST_CODE = 0;
+ public final static int BROWSE_SAVE_WEBPAGE_REQUEST_CODE = 1;
+ private final int BROWSE_FILE_UPLOAD_REQUEST_CODE = 2;
+ // The proxy mode is public static so it can be accessed from `ProxyHelper()`.
+ // It is also used in `onRestart()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, `applyAppSettings()`, and `applyProxy()`.
+ // It will be updated in `applyAppSettings()`, but it needs to be initialized here or the first run of `onPrepareOptionsMenu()` crashes.
+ public static String proxyMode = ProxyHelper.NONE;
+
+
+ // The permission result request codes are used in `onCreateContextMenu()`, `onRequestPermissionResult()`, `onSaveWebpage()`, `onCloseStoragePermissionDialog()`, and `initializeWebView()`.
+ private final int PERMISSION_OPEN_REQUEST_CODE = 0;
+ private final int PERMISSION_SAVE_URL_REQUEST_CODE = 1;
+ private final int PERMISSION_SAVE_AS_ARCHIVE_REQUEST_CODE = 2;
+ private final int PERMISSION_SAVE_AS_IMAGE_REQUEST_CODE = 3;
// The current WebView is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onRestart()`, `onCreateContextMenu()`, `findPreviousOnPage()`,
- // `findNextOnPage()`, `closeFindOnPage()`, `loadUrlFromTextBox()`, `onSslMismatchBack()`, `applyProxyThroughOrbot()`, and `applyDomainSettings()`.
+ // `findNextOnPage()`, `closeFindOnPage()`, `loadUrlFromTextBox()`, `onSslMismatchBack()`, `applyProxy()`, and `applyDomainSettings()`.
private NestedScrollWebView currentWebView;
// `customHeader` is used in `onCreate()`, `onOptionsItemSelected()`, `onCreateContextMenu()`, and `loadUrl()`.
private final Map<String, String> customHeaders = new HashMap<>();
- // The search URL is set in `applyProxyThroughOrbot()` and used in `onCreate()`, `onNewIntent()`, `loadURLFromTextBox()`, and `initializeWebView()`.
+ // The search URL is set in `applyAppSettings()` and used in `onNewIntent()`, `loadUrlFromTextBox()`, `initializeApp()`, and `initializeWebView()`.
private String searchURL;
// The options menu is set in `onCreateOptionsMenu()` and used in `onOptionsItemSelected()`, `updatePrivacyIcons()`, and `initializeWebView()`.
// `webViewDefaultUserAgent` is used in `onCreate()` and `onPrepareOptionsMenu()`.
private String webViewDefaultUserAgent;
- // `proxyThroughOrbot` is used in `onRestart()`, `onOptionsItemSelected()`, `applyAppSettings()`, and `applyProxyThroughOrbot()`.
- private boolean proxyThroughOrbot;
-
// The incognito mode is set in `applyAppSettings()` and used in `initializeWebView()`.
private boolean incognitoModeEnabled;
// `orbotStatusBroadcastReceiver` is used in `onCreate()` and `onDestroy()`.
private BroadcastReceiver orbotStatusBroadcastReceiver;
- // `waitingForOrbot` is used in `onCreate()`, `onResume()`, and `applyProxyThroughOrbot()`.
- private boolean waitingForOrbot;
+ // The waiting for proxy boolean is used in `onResume()`, `initializeApp()` and `applyProxy()`.
+ private boolean waitingForProxy = false;
// The action bar drawer toggle is initialized in `onCreate()` and used in `onResume()`.
private ActionBarDrawerToggle actionBarDrawerToggle;
private boolean sanitizeFacebookClickIds;
private boolean sanitizeTwitterAmpRedirects;
- // The download strings are used in `onCreate()`, `onRequestPermissionResult()` and `initializeWebView()`.
- private String downloadUrl;
- private String downloadContentDisposition;
- private long downloadContentLength;
-
- // `downloadImageUrl` is used in `onCreateContextMenu()` and `onRequestPermissionResult()`.
- private String downloadImageUrl;
-
- // The save website image file path string is used in `onSaveWebpageImage()` and `onRequestPermissionResult()`
- private String saveWebsiteImageFilePath;
-
- // The permission result request codes are used in `onCreateContextMenu()`, `onCloseDownloadLocationPermissionDialog()`, `onRequestPermissionResult()`, `onSaveWebpageImage()`,
- // `onCloseStoragePermissionDialog()`, and `initializeWebView()`.
- private final int DOWNLOAD_FILE_REQUEST_CODE = 1;
- private final int DOWNLOAD_IMAGE_REQUEST_CODE = 2;
- private final int SAVE_WEBPAGE_IMAGE_REQUEST_CODE = 3;
+ // The file path strings are used in `onSaveWebpage()` and `onRequestPermissionResult()`
+ private String openFilePath;
+ private String saveWebpageUrl;
+ private String saveWebpageFilePath;
@Override
// Remove the warning about needing to override `performClick()` when using an `OnTouchListener` with `WebView`.
@SuppressLint("ClickableViewAccessibility")
protected void onCreate(Bundle savedInstanceState) {
+ // Enable the drawing of the entire webpage. This makes it possible to save a website image. This must be done before anything else happens with the WebView.
if (Build.VERSION.SDK_INT >= 21) {
WebView.enableSlowWholeDocumentDraw();
}
@Override
protected void onNewIntent(Intent intent) {
+ // Run the default commands.
+ super.onNewIntent(intent);
+
// Replace the intent that started the app with this one.
setIntent(intent);
addNewTab(url, true);
} else { // Load the URL in the current tab.
// Make it so.
- loadUrl(url);
+ loadUrl(currentWebView, url);
}
// Get a handle for the drawer layout.
// Run the default commands.
super.onRestart();
- // Make sure Orbot is running if Privacy Browser is proxying through Orbot.
- if (proxyThroughOrbot) {
- // Request Orbot to start. If Orbot is already running no hard will be caused by this request.
- Intent orbotIntent = new Intent("org.torproject.android.intent.action.START");
-
- // Send the intent to the Orbot package.
- orbotIntent.setPackage("org.torproject.android");
-
- // Make it so.
- sendBroadcast(orbotIntent);
- }
-
// Apply the app settings if returning from the Settings activity.
if (reapplyAppSettingsOnRestart) {
// Reset the reapply app settings on restart tracker.
// Load the URL on restart (used when loading a bookmark).
if (loadUrlOnRestart) {
// Load the specified URL.
- loadUrl(urlToLoadOnRestart);
+ loadUrl(currentWebView, urlToLoadOnRestart);
// Reset the load on restart tracker.
loadUrlOnRestart = false;
// Run the default commands.
super.onResume();
+ // Resume any WebViews.
for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
// Get the WebView tab fragment.
WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i);
}
}
- // Display a message to the user if waiting for Orbot.
- if (waitingForOrbot && !orbotStatus.equals("ON")) {
- // Disable the wide view port so that the waiting for Orbot text is displayed correctly.
- currentWebView.getSettings().setUseWideViewPort(false);
-
- // Load a waiting page. `null` specifies no encoding, which defaults to ASCII.
- currentWebView.loadData("<html><body><br/><center><h1>" + getString(R.string.waiting_for_orbot) + "</h1></center></body></html>", "text/html", null);
+ // Reapply the proxy settings if the system is using a proxy. This redisplays the appropriate alert dialog.
+ if (!proxyMode.equals(ProxyHelper.NONE)) {
+ applyProxy(false);
}
- if (displayingFullScreenVideo || inFullScreenBrowsingMode) {
+ // Reapply any system UI flags and the ad in the free flavor.
+ if (displayingFullScreenVideo || inFullScreenBrowsingMode) { // The system is displaying a website or a video in full screen mode.
// Get a handle for the root frame layouts.
FrameLayout rootFrameLayout = findViewById(R.id.root_framelayout);
*/
rootFrameLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
- } else if (BuildConfig.FLAVOR.contentEquals("free")) { // Resume the adView for the free flavor.
+ } else if (BuildConfig.FLAVOR.contentEquals("free")) { // The system in not in full screen mode.
// Resume the ad.
AdHelper.resumeAd(findViewById(R.id.adview));
}
@Override
public void onDestroy() {
- // Unregister the Orbot status broadcast receiver.
+ // Unregister the orbot status broadcast receiver.
this.unregisterReceiver(orbotStatusBroadcastReceiver);
// Close the bookmarks cursor and database.
MenuItem ultraListMenuItem = menu.findItem(R.id.ultralist);
MenuItem ultraPrivacyMenuItem = menu.findItem(R.id.ultraprivacy);
MenuItem blockAllThirdPartyRequestsMenuItem = menu.findItem(R.id.block_all_third_party_requests);
+ MenuItem proxyMenuItem = menu.findItem(R.id.proxy);
+ MenuItem userAgentMenuItem = menu.findItem(R.id.user_agent);
MenuItem fontSizeMenuItem = menu.findItem(R.id.font_size);
MenuItem swipeToRefreshMenuItem = menu.findItem(R.id.swipe_to_refresh);
MenuItem wideViewportMenuItem = menu.findItem(R.id.wide_viewport);
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);
// Get a handle for the cookie manager.
CookieManager cookieManager = CookieManager.getInstance();
domStorageMenuItem.setEnabled(currentWebView.getSettings().getJavaScriptEnabled());
}
- // Set the status of the menu item checkboxes.
+ // Set the checked status of the first party cookies menu item.
firstPartyCookiesMenuItem.setChecked(cookieManager.acceptCookie());
- proxyThroughOrbotMenuItem.setChecked(proxyThroughOrbot);
// Enable Clear Cookies if there are any.
clearCookiesMenuItem.setEnabled(cookieManager.hasCookies());
File localStorageDirectory = new File (privateDataDirectoryString + "/app_webview/Local Storage/");
int localStorageDirectoryNumberOfFiles = 0;
if (localStorageDirectory.exists()) {
- localStorageDirectoryNumberOfFiles = localStorageDirectory.list().length;
+ // `Objects.requireNonNull` removes a lint warning that `localStorageDirectory.list` might produce a null pointed exception if it is dereferenced.
+ localStorageDirectoryNumberOfFiles = Objects.requireNonNull(localStorageDirectory.list()).length;
}
// Get a count of the number of files in the IndexedDB directory.
File indexedDBDirectory = new File (privateDataDirectoryString + "/app_webview/IndexedDB");
int indexedDBDirectoryNumberOfFiles = 0;
if (indexedDBDirectory.exists()) {
- indexedDBDirectoryNumberOfFiles = indexedDBDirectory.list().length;
+ // `Objects.requireNonNull` removes a lint warning that `indexedDBDirectory.list` might produce a null pointed exception if it is dereferenced.
+ indexedDBDirectoryNumberOfFiles = Objects.requireNonNull(indexedDBDirectory.list()).length;
}
// Enable Clear DOM Storage if there is any.
// Disable Fanboy's Social Blocking List menu item if Fanboy's Annoyance List is checked.
fanboysSocialBlockingListMenuItem.setEnabled(!fanboysAnnoyanceListMenuItem.isChecked());
+ // Set the proxy title and check the applied proxy.
+ switch (proxyMode) {
+ case ProxyHelper.NONE:
+ // Set the proxy title.
+ proxyMenuItem.setTitle(getString(R.string.proxy) + " - " + getString(R.string.proxy_none));
+
+ // Check the proxy None radio button.
+ menu.findItem(R.id.proxy_none).setChecked(true);
+ break;
+
+ case ProxyHelper.TOR:
+ // Set the proxy title.
+ proxyMenuItem.setTitle(getString(R.string.proxy) + " - " + getString(R.string.proxy_tor));
+
+ // Check the proxy Tor radio button.
+ menu.findItem(R.id.proxy_tor).setChecked(true);
+ break;
+
+ case ProxyHelper.I2P:
+ // Set the proxy title.
+ proxyMenuItem.setTitle(getString(R.string.proxy) + " - " + getString(R.string.proxy_i2p));
+
+ // Check the proxy I2P radio button.
+ menu.findItem(R.id.proxy_i2p).setChecked(true);
+ break;
+
+ case ProxyHelper.CUSTOM:
+ // Set the proxy title.
+ proxyMenuItem.setTitle(getString(R.string.proxy) + " - " + getString(R.string.proxy_custom));
+
+ // Check the proxy Custom radio button.
+ menu.findItem(R.id.proxy_custom).setChecked(true);
+ break;
+ }
+
// Select the current user agent menu item. A switch statement cannot be used because the user agents are not compile time constants.
if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[0])) { // Privacy Browser.
+ // Update the user agent menu item title.
+ userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_privacy_browser));
+
+ // Select the Privacy Browser radio box.
menu.findItem(R.id.user_agent_privacy_browser).setChecked(true);
} else if (currentUserAgent.equals(webViewDefaultUserAgent)) { // WebView Default.
+ // Update the user agent menu item title.
+ userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_webview_default));
+
+ // Select the WebView Default radio box.
menu.findItem(R.id.user_agent_webview_default).setChecked(true);
} else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[2])) { // Firefox on Android.
+ // Update the user agent menu item title.
+ userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_firefox_on_android));
+
+ // Select the Firefox on Android radio box.
menu.findItem(R.id.user_agent_firefox_on_android).setChecked(true);
} else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[3])) { // Chrome on Android.
+ // Update the user agent menu item title.
+ userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_chrome_on_android));
+
+ // Select the Chrome on Android radio box.
menu.findItem(R.id.user_agent_chrome_on_android).setChecked(true);
} else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[4])) { // Safari on iOS.
+ // Update the user agent menu item title.
+ userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_safari_on_ios));
+
+ // Select the Safari on iOS radio box.
menu.findItem(R.id.user_agent_safari_on_ios).setChecked(true);
} else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[5])) { // Firefox on Linux.
+ // Update the user agent menu item title.
+ userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_firefox_on_linux));
+
+ // Select the Firefox on Linux radio box.
menu.findItem(R.id.user_agent_firefox_on_linux).setChecked(true);
} else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[6])) { // Chromium on Linux.
+ // Update the user agent menu item title.
+ userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_chromium_on_linux));
+
+ // Select the Chromium on Linux radio box.
menu.findItem(R.id.user_agent_chromium_on_linux).setChecked(true);
} else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[7])) { // Firefox on Windows.
+ // Update the user agent menu item title.
+ userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_firefox_on_windows));
+
+ // Select the Firefox on Windows radio box.
menu.findItem(R.id.user_agent_firefox_on_windows).setChecked(true);
} else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[8])) { // Chrome on Windows.
+ // Update the user agent menu item title.
+ userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_chrome_on_windows));
+
+ // Select the Chrome on Windows radio box.
menu.findItem(R.id.user_agent_chrome_on_windows).setChecked(true);
} else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[9])) { // Edge on Windows.
+ // Update the user agent menu item title.
+ userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_edge_on_windows));
+
+ // Select the Edge on Windows radio box.
menu.findItem(R.id.user_agent_edge_on_windows).setChecked(true);
} else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[10])) { // Internet Explorer on Windows.
+ // Update the user agent menu item title.
+ userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_internet_explorer_on_windows));
+
+ // Select the Internet on Windows radio box.
menu.findItem(R.id.user_agent_internet_explorer_on_windows).setChecked(true);
} else if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[11])) { // Safari on macOS.
+ // Update the user agent menu item title.
+ userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_safari_on_macos));
+
+ // Select the Safari on macOS radio box.
menu.findItem(R.id.user_agent_safari_on_macos).setChecked(true);
} else { // Custom user agent.
- menu.findItem(R.id.user_agent_custom).setChecked(true);
- }
-
- // Instantiate the font size title and the selected font size menu item.
- String fontSizeTitle;
- MenuItem selectedFontSizeMenuItem;
+ // Update the user agent menu item title.
+ userAgentMenuItem.setTitle(getString(R.string.options_user_agent) + " - " + getString(R.string.user_agent_custom));
- // Prepare the font size title and current size menu item.
- switch (fontSize) {
- case 25:
- fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.twenty_five_percent);
- selectedFontSizeMenuItem = menu.findItem(R.id.font_size_twenty_five_percent);
- break;
-
- case 50:
- fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.fifty_percent);
- selectedFontSizeMenuItem = menu.findItem(R.id.font_size_fifty_percent);
- break;
-
- case 75:
- fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.seventy_five_percent);
- selectedFontSizeMenuItem = menu.findItem(R.id.font_size_seventy_five_percent);
- break;
-
- case 100:
- fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.one_hundred_percent);
- selectedFontSizeMenuItem = menu.findItem(R.id.font_size_one_hundred_percent);
- break;
-
- case 125:
- fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.one_hundred_twenty_five_percent);
- selectedFontSizeMenuItem = menu.findItem(R.id.font_size_one_hundred_twenty_five_percent);
- break;
-
- case 150:
- fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.one_hundred_fifty_percent);
- selectedFontSizeMenuItem = menu.findItem(R.id.font_size_one_hundred_fifty_percent);
- break;
-
- case 175:
- fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.one_hundred_seventy_five_percent);
- selectedFontSizeMenuItem = menu.findItem(R.id.font_size_one_hundred_seventy_five_percent);
- break;
-
- case 200:
- fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.two_hundred_percent);
- selectedFontSizeMenuItem = menu.findItem(R.id.font_size_two_hundred_percent);
- break;
-
- default:
- fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.one_hundred_percent);
- selectedFontSizeMenuItem = menu.findItem(R.id.font_size_one_hundred_percent);
- break;
+ // Select the Custom radio box.
+ menu.findItem(R.id.user_agent_custom).setChecked(true);
}
- // Set the font size title and select the current size menu item.
- fontSizeMenuItem.setTitle(fontSizeTitle);
- selectedFontSizeMenuItem.setChecked(true);
+ // Set the font size title.
+ fontSizeMenuItem.setTitle(getString(R.string.font_size) + " - " + fontSize + "%");
// Run all the other default commands.
super.onPrepareOptionsMenu(menu);
// Consume the event.
return true;
- case R.id.add_or_edit_domain:
- if (currentWebView.getDomainSettingsApplied()) { // Edit the current domain settings.
- // Reapply the domain settings on returning to `MainWebViewActivity`.
- reapplyDomainSettingsOnRestart = true;
-
- // Create an intent to launch the domains activity.
- Intent domainsIntent = new Intent(this, DomainsActivity.class);
-
- // Add the extra information to the intent.
- domainsIntent.putExtra("load_domain", currentWebView.getDomainSettingsDatabaseId());
- domainsIntent.putExtra("close_on_back", true);
- domainsIntent.putExtra("current_url", currentWebView.getUrl());
-
- // Get the current certificate.
- SslCertificate sslCertificate = currentWebView.getCertificate();
-
- // Check to see if the SSL certificate is populated.
- if (sslCertificate != null) {
- // Extract the certificate to strings.
- String issuedToCName = sslCertificate.getIssuedTo().getCName();
- String issuedToOName = sslCertificate.getIssuedTo().getOName();
- String issuedToUName = sslCertificate.getIssuedTo().getUName();
- String issuedByCName = sslCertificate.getIssuedBy().getCName();
- String issuedByOName = sslCertificate.getIssuedBy().getOName();
- String issuedByUName = sslCertificate.getIssuedBy().getUName();
- long startDateLong = sslCertificate.getValidNotBeforeDate().getTime();
- long endDateLong = sslCertificate.getValidNotAfterDate().getTime();
-
- // Add the certificate to the intent.
- domainsIntent.putExtra("ssl_issued_to_cname", issuedToCName);
- domainsIntent.putExtra("ssl_issued_to_oname", issuedToOName);
- domainsIntent.putExtra("ssl_issued_to_uname", issuedToUName);
- domainsIntent.putExtra("ssl_issued_by_cname", issuedByCName);
- domainsIntent.putExtra("ssl_issued_by_oname", issuedByOName);
- domainsIntent.putExtra("ssl_issued_by_uname", issuedByUName);
- domainsIntent.putExtra("ssl_start_date", startDateLong);
- domainsIntent.putExtra("ssl_end_date", endDateLong);
- }
-
- // Check to see if the current IP addresses have been received.
- if (currentWebView.hasCurrentIpAddresses()) {
- // Add the current IP addresses to the intent.
- domainsIntent.putExtra("current_ip_addresses", currentWebView.getCurrentIpAddresses());
- }
-
- // Make it so.
- startActivity(domainsIntent);
- } else { // Add a new domain.
- // Apply the new domain settings on returning to `MainWebViewActivity`.
- reapplyDomainSettingsOnRestart = true;
-
- // Get the current domain
- Uri currentUri = Uri.parse(currentWebView.getUrl());
- String currentDomain = currentUri.getHost();
-
- // Initialize the database handler. The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`.
- DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(this, null, null, 0);
-
- // Create the domain and store the database ID.
- int newDomainDatabaseId = domainsDatabaseHelper.addDomain(currentDomain);
-
- // Create an intent to launch the domains activity.
- Intent domainsIntent = new Intent(this, DomainsActivity.class);
-
- // Add the extra information to the intent.
- domainsIntent.putExtra("load_domain", newDomainDatabaseId);
- domainsIntent.putExtra("close_on_back", true);
- domainsIntent.putExtra("current_url", currentWebView.getUrl());
-
- // Get the current certificate.
- SslCertificate sslCertificate = currentWebView.getCertificate();
-
- // Check to see if the SSL certificate is populated.
- if (sslCertificate != null) {
- // Extract the certificate to strings.
- String issuedToCName = sslCertificate.getIssuedTo().getCName();
- String issuedToOName = sslCertificate.getIssuedTo().getOName();
- String issuedToUName = sslCertificate.getIssuedTo().getUName();
- String issuedByCName = sslCertificate.getIssuedBy().getCName();
- String issuedByOName = sslCertificate.getIssuedBy().getOName();
- String issuedByUName = sslCertificate.getIssuedBy().getUName();
- long startDateLong = sslCertificate.getValidNotBeforeDate().getTime();
- long endDateLong = sslCertificate.getValidNotAfterDate().getTime();
+ case R.id.refresh:
+ if (menuItem.getTitle().equals(getString(R.string.refresh))) { // The refresh button was pushed.
+ // Reload the current WebView.
+ currentWebView.reload();
+ } else { // The stop button was pushed.
+ // Stop the loading of the WebView.
+ currentWebView.stopLoading();
+ }
- // Add the certificate to the intent.
- domainsIntent.putExtra("ssl_issued_to_cname", issuedToCName);
- domainsIntent.putExtra("ssl_issued_to_oname", issuedToOName);
- domainsIntent.putExtra("ssl_issued_to_uname", issuedToUName);
- domainsIntent.putExtra("ssl_issued_by_cname", issuedByCName);
- domainsIntent.putExtra("ssl_issued_by_oname", issuedByOName);
- domainsIntent.putExtra("ssl_issued_by_uname", issuedByUName);
- domainsIntent.putExtra("ssl_start_date", startDateLong);
- domainsIntent.putExtra("ssl_end_date", endDateLong);
- }
+ // Consume the event.
+ return true;
- // Check to see if the current IP addresses have been received.
- if (currentWebView.hasCurrentIpAddresses()) {
- // Add the current IP addresses to the intent.
- domainsIntent.putExtra("current_ip_addresses", currentWebView.getCurrentIpAddresses());
- }
+ case R.id.bookmarks:
+ // Get a handle for the drawer layout.
+ DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
- // Make it so.
- startActivity(domainsIntent);
- }
+ // Open the bookmarks drawer.
+ drawerLayout.openDrawer(GravityCompat.END);
// Consume the event.
return true;
// Consume the event.
return true;
+ case R.id.proxy_none:
+ // Update the proxy mode.
+ proxyMode = ProxyHelper.NONE;
+
+ // Apply the proxy mode.
+ applyProxy(true);
+
+ // Consume the event.
+ return true;
+
+ case R.id.proxy_tor:
+ // Update the proxy mode.
+ proxyMode = ProxyHelper.TOR;
+
+ // Apply the proxy mode.
+ applyProxy(true);
+
+ // Consume the event.
+ return true;
+
+ case R.id.proxy_i2p:
+ // Update the proxy mode.
+ proxyMode = ProxyHelper.I2P;
+
+ // Apply the proxy mode.
+ applyProxy(true);
+
+ // Consume the event.
+ return true;
+
+ case R.id.proxy_custom:
+ // Update the proxy mode.
+ proxyMode = ProxyHelper.CUSTOM;
+
+ // Apply the proxy mode.
+ applyProxy(true);
+
+ // Consume the event.
+ return true;
+
case R.id.user_agent_privacy_browser:
// Update the user agent.
currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[0]);
// Consume the event.
return true;
- case R.id.font_size_twenty_five_percent:
- // Set the font size.
- currentWebView.getSettings().setTextZoom(25);
-
- // Consume the event.
- return true;
-
- case R.id.font_size_fifty_percent:
- // Set the font size.
- currentWebView.getSettings().setTextZoom(50);
-
- // Consume the event.
- return true;
-
- case R.id.font_size_seventy_five_percent:
- // Set the font size.
- currentWebView.getSettings().setTextZoom(75);
-
- // Consume the event.
- return true;
-
- case R.id.font_size_one_hundred_percent:
- // Set the font size.
- currentWebView.getSettings().setTextZoom(100);
-
- // Consume the event.
- return true;
-
- case R.id.font_size_one_hundred_twenty_five_percent:
- // Set the font size.
- currentWebView.getSettings().setTextZoom(125);
-
- // Consume the event.
- return true;
+ case R.id.font_size:
+ // Instantiate the font size dialog.
+ DialogFragment fontSizeDialogFragment = FontSizeDialog.displayDialog(currentWebView.getSettings().getTextZoom());
- case R.id.font_size_one_hundred_fifty_percent:
- // Set the font size.
- currentWebView.getSettings().setTextZoom(150);
-
- // Consume the event.
- return true;
-
- case R.id.font_size_one_hundred_seventy_five_percent:
- // Set the font size.
- currentWebView.getSettings().setTextZoom(175);
-
- // Consume the event.
- return true;
-
- case R.id.font_size_two_hundred_percent:
- // Set the font size.
- currentWebView.getSettings().setTextZoom(200);
+ // Show the font size dialog.
+ fontSizeDialogFragment.show(getSupportFragmentManager(), getString(R.string.font_size));
// Consume the event.
return true;
// Consume the event.
return true;
+ case R.id.save_url:
+ // Instantiate the save dialog.
+ DialogFragment saveDialogFragment = SaveDialog.saveUrl(StoragePermissionDialog.SAVE_URL, currentWebView.getCurrentUrl(), currentWebView.getSettings().getUserAgentString(),
+ currentWebView.getAcceptFirstPartyCookies());
+
+ // Show the save dialog. It must be named `save_dialog` so that the file picked can update the file name.
+ saveDialogFragment.show(getSupportFragmentManager(), getString(R.string.save_dialog));
+
+ // Consume the event.
+ return true;
+
+ case R.id.save_as_archive:
+ // Instantiate the save webpage archive dialog.
+ DialogFragment saveWebpageArchiveDialogFragment = SaveDialog.saveUrl(StoragePermissionDialog.SAVE_AS_ARCHIVE, currentWebView.getCurrentUrl(), currentWebView.getSettings().getUserAgentString(),
+ currentWebView.getAcceptFirstPartyCookies());
+
+ // Show the save webpage archive dialog. It must be named `save_dialog` so that the file picked can update the file name.
+ saveWebpageArchiveDialogFragment.show(getSupportFragmentManager(), getString(R.string.save_dialog));
+
+ // Consume the event.
+ return true;
+
case R.id.save_as_image:
- // Instantiate the save webpage image dialog.
- DialogFragment saveWebpageImageDialogFragment = new SaveWebpageImageDialog();
+ // Instantiate the save webpage image dialog. It must be named `save_webpage` so that the file picked can update the file name.
+ DialogFragment saveWebpageImageDialogFragment = SaveDialog.saveUrl(StoragePermissionDialog.SAVE_AS_IMAGE, currentWebView.getCurrentUrl(), currentWebView.getSettings().getUserAgentString(),
+ currentWebView.getAcceptFirstPartyCookies());
- // Show the save webpage image dialog.
- saveWebpageImageDialogFragment.show(getSupportFragmentManager(), getString(R.string.save_as_image));
+ // Show the save webpage image dialog. It must be named `save_dialog` so that the file picked can update the file name.
+ saveWebpageImageDialogFragment.show(getSupportFragmentManager(), getString(R.string.save_dialog));
// Consume the event.
return true;
// Consume the event.
return true;
- case R.id.proxy_through_orbot:
- // Toggle the proxy through Orbot variable.
- proxyThroughOrbot = !proxyThroughOrbot;
+ case R.id.add_or_edit_domain:
+ if (currentWebView.getDomainSettingsApplied()) { // Edit the current domain settings.
+ // Reapply the domain settings on returning to `MainWebViewActivity`.
+ reapplyDomainSettingsOnRestart = true;
+
+ // Create an intent to launch the domains activity.
+ Intent domainsIntent = new Intent(this, DomainsActivity.class);
- // Apply the proxy through Orbot settings.
- applyProxyThroughOrbot(true);
+ // Add the extra information to the intent.
+ domainsIntent.putExtra("load_domain", currentWebView.getDomainSettingsDatabaseId());
+ domainsIntent.putExtra("close_on_back", true);
+ domainsIntent.putExtra("current_url", currentWebView.getUrl());
- // Consume the event.
- return true;
+ // Get the current certificate.
+ SslCertificate sslCertificate = currentWebView.getCertificate();
- case R.id.refresh:
- if (menuItem.getTitle().equals(getString(R.string.refresh))) { // The refresh button was pushed.
- // Reload the current WebView.
- currentWebView.reload();
- } else { // The stop button was pushed.
- // Stop the loading of the WebView.
- currentWebView.stopLoading();
+ // Check to see if the SSL certificate is populated.
+ if (sslCertificate != null) {
+ // Extract the certificate to strings.
+ String issuedToCName = sslCertificate.getIssuedTo().getCName();
+ String issuedToOName = sslCertificate.getIssuedTo().getOName();
+ String issuedToUName = sslCertificate.getIssuedTo().getUName();
+ String issuedByCName = sslCertificate.getIssuedBy().getCName();
+ String issuedByOName = sslCertificate.getIssuedBy().getOName();
+ String issuedByUName = sslCertificate.getIssuedBy().getUName();
+ long startDateLong = sslCertificate.getValidNotBeforeDate().getTime();
+ long endDateLong = sslCertificate.getValidNotAfterDate().getTime();
+
+ // Add the certificate to the intent.
+ domainsIntent.putExtra("ssl_issued_to_cname", issuedToCName);
+ domainsIntent.putExtra("ssl_issued_to_oname", issuedToOName);
+ domainsIntent.putExtra("ssl_issued_to_uname", issuedToUName);
+ domainsIntent.putExtra("ssl_issued_by_cname", issuedByCName);
+ domainsIntent.putExtra("ssl_issued_by_oname", issuedByOName);
+ domainsIntent.putExtra("ssl_issued_by_uname", issuedByUName);
+ domainsIntent.putExtra("ssl_start_date", startDateLong);
+ domainsIntent.putExtra("ssl_end_date", endDateLong);
+ }
+
+ // Check to see if the current IP addresses have been received.
+ if (currentWebView.hasCurrentIpAddresses()) {
+ // Add the current IP addresses to the intent.
+ domainsIntent.putExtra("current_ip_addresses", currentWebView.getCurrentIpAddresses());
+ }
+
+ // Make it so.
+ startActivity(domainsIntent);
+ } else { // Add a new domain.
+ // Apply the new domain settings on returning to `MainWebViewActivity`.
+ reapplyDomainSettingsOnRestart = true;
+
+ // Get the current domain
+ Uri currentUri = Uri.parse(currentWebView.getUrl());
+ String currentDomain = currentUri.getHost();
+
+ // Initialize the database handler. The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`.
+ DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(this, null, null, 0);
+
+ // Create the domain and store the database ID.
+ int newDomainDatabaseId = domainsDatabaseHelper.addDomain(currentDomain);
+
+ // Create an intent to launch the domains activity.
+ Intent domainsIntent = new Intent(this, DomainsActivity.class);
+
+ // Add the extra information to the intent.
+ domainsIntent.putExtra("load_domain", newDomainDatabaseId);
+ domainsIntent.putExtra("close_on_back", true);
+ domainsIntent.putExtra("current_url", currentWebView.getUrl());
+
+ // Get the current certificate.
+ SslCertificate sslCertificate = currentWebView.getCertificate();
+
+ // Check to see if the SSL certificate is populated.
+ if (sslCertificate != null) {
+ // Extract the certificate to strings.
+ String issuedToCName = sslCertificate.getIssuedTo().getCName();
+ String issuedToOName = sslCertificate.getIssuedTo().getOName();
+ String issuedToUName = sslCertificate.getIssuedTo().getUName();
+ String issuedByCName = sslCertificate.getIssuedBy().getCName();
+ String issuedByOName = sslCertificate.getIssuedBy().getOName();
+ String issuedByUName = sslCertificate.getIssuedBy().getUName();
+ long startDateLong = sslCertificate.getValidNotBeforeDate().getTime();
+ long endDateLong = sslCertificate.getValidNotAfterDate().getTime();
+
+ // Add the certificate to the intent.
+ domainsIntent.putExtra("ssl_issued_to_cname", issuedToCName);
+ domainsIntent.putExtra("ssl_issued_to_oname", issuedToOName);
+ domainsIntent.putExtra("ssl_issued_to_uname", issuedToUName);
+ domainsIntent.putExtra("ssl_issued_by_cname", issuedByCName);
+ domainsIntent.putExtra("ssl_issued_by_oname", issuedByOName);
+ domainsIntent.putExtra("ssl_issued_by_uname", issuedByUName);
+ domainsIntent.putExtra("ssl_start_date", startDateLong);
+ domainsIntent.putExtra("ssl_end_date", endDateLong);
+ }
+
+ // Check to see if the current IP addresses have been received.
+ if (currentWebView.hasCurrentIpAddresses()) {
+ // Add the current IP addresses to the intent.
+ domainsIntent.putExtra("current_ip_addresses", currentWebView.getCurrentIpAddresses());
+ }
+
+ // Make it so.
+ startActivity(domainsIntent);
}
// Consume the event.
break;
case R.id.home:
- // Select the homepage based on the proxy through Orbot status.
- if (proxyThroughOrbot) {
- // Load the Tor homepage.
- loadUrl(sharedPreferences.getString("tor_homepage", getString(R.string.tor_homepage_default_value)));
- } else {
- // Load the normal homepage.
- loadUrl(sharedPreferences.getString("homepage", getString(R.string.homepage_default_value)));
- }
+ // Load the homepage.
+ loadUrl(currentWebView, sharedPreferences.getString("homepage", getString(R.string.homepage_default_value)));
break;
case R.id.back:
// Get the previous entry URL.
String previousUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() - 1).getUrl();
- // Reset the current domain name so that navigation works if third-party requests are blocked.
- currentWebView.resetCurrentDomainName();
-
// Apply the domain settings.
applyDomainSettings(currentWebView, previousUrl, false, false);
// Get the next entry URL.
String nextUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() + 1).getUrl();
- // Reset the current domain name so that navigation works if third-party requests are blocked.
- currentWebView.resetCurrentDomainName();
-
// Apply the domain settings.
applyDomainSettings(currentWebView, nextUrl, false, false);
urlHistoryDialogFragment.show(getSupportFragmentManager(), getString(R.string.history));
break;
+ case R.id.open:
+ // Instantiate the open file dialog.
+ DialogFragment openDialogFragment = new OpenDialog();
+
+ // Show the open file dialog.
+ openDialogFragment.show(getSupportFragmentManager(), getString(R.string.open));
+ break;
+
case R.id.requests:
// Populate the resource requests.
RequestsActivity.resourceRequests = currentWebView.getResourceRequests();
// Launch as a new task so that Download Manager and Privacy Browser show as separate windows in the recent tasks list.
downloadManagerIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ // Make it so.
startActivity(downloadManagerIntent);
break;
}
@Override
- public void onConfigurationChanged(Configuration newConfig) {
+ public void onConfigurationChanged(@NonNull Configuration newConfig) {
// Run the default commands.
super.onConfigurationChanged(newConfig);
// Get handles for the system managers.
final ClipboardManager clipboardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
- FragmentManager fragmentManager = getSupportFragmentManager();
- SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
// Remove the lint errors below that the clipboard manager might be null.
assert clipboardManager != null;
// Add an Open in New Tab entry.
menu.add(R.string.open_in_new_tab).setOnMenuItemClickListener((MenuItem item) -> {
- // Load the link URL in a new tab.
+ // Load the link URL in a new tab and move to it.
+ addNewTab(linkUrl, true);
+
+ // Consume the event.
+ return true;
+ });
+
+ // Add an Open in Background entry.
+ menu.add(R.string.open_in_background).setOnMenuItemClickListener((MenuItem item) -> {
+ // Load the link URL in a new tab but do not move to it.
addNewTab(linkUrl, false);
// Consume the event.
return true;
});
- // Add a Download URL entry.
- menu.add(R.string.download_url).setOnMenuItemClickListener((MenuItem item) -> {
- // Check if the download should be processed by an external app.
- if (sharedPreferences.getBoolean("download_with_external_app", false)) { // 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(fragmentManager, 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.
- DialogFragment downloadFileDialogFragment = DownloadFileDialog.fromUrl(linkUrl, "none", -1);
+ // Add a Save URL entry.
+ menu.add(R.string.save_url).setOnMenuItemClickListener((MenuItem item) -> {
+ // Instantiate the save dialog.
+ DialogFragment saveDialogFragment = SaveDialog.saveUrl(StoragePermissionDialog.SAVE_URL, linkUrl, currentWebView.getSettings().getUserAgentString(),
+ currentWebView.getAcceptFirstPartyCookies());
- // Show the download file alert dialog.
- downloadFileDialogFragment.show(fragmentManager, getString(R.string.download));
- }
- }
+ // Show the save dialog. It must be named `save_dialog` so that the file picker can update the file name.
+ saveDialogFragment.show(getSupportFragmentManager(), getString(R.string.save_dialog));
// Consume the event.
return true;
// Add an Open in New Tab entry.
menu.add(R.string.open_image_in_new_tab).setOnMenuItemClickListener((MenuItem item) -> {
// Load the image in a new tab.
- addNewTab(imageUrl, false);
+ addNewTab(imageUrl, true);
// Consume the event.
return true;
// Add a View Image entry.
menu.add(R.string.view_image).setOnMenuItemClickListener(item -> {
// Load the image in the current tab.
- loadUrl(imageUrl);
+ loadUrl(currentWebView, imageUrl);
// Consume the event.
return true;
});
- // Add a Download Image entry.
- menu.add(R.string.download_image).setOnMenuItemClickListener((MenuItem item) -> {
- // Check if the download should be processed by an external app.
- if (sharedPreferences.getBoolean("download_with_external_app", false)) { // 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(fragmentManager, 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.
- DialogFragment downloadImageDialogFragment = DownloadImageDialog.imageUrl(imageUrl);
+ // Add a Save Image entry.
+ menu.add(R.string.save_image).setOnMenuItemClickListener((MenuItem item) -> {
+ // Instantiate the save dialog.
+ DialogFragment saveDialogFragment = SaveDialog.saveUrl(StoragePermissionDialog.SAVE_URL, imageUrl, currentWebView.getSettings().getUserAgentString(),
+ currentWebView.getAcceptFirstPartyCookies());
- // Show the download image alert dialog.
- downloadImageDialogFragment.show(fragmentManager, getString(R.string.download));
- }
- }
+ // Show the save dialog. It must be named `save_dialog` so that the file picked can update the file name.
+ saveDialogFragment.show(getSupportFragmentManager(), getString(R.string.save_dialog));
// Consume the event.
return true;
// Add an Open in New Tab entry.
menu.add(R.string.open_in_new_tab).setOnMenuItemClickListener((MenuItem item) -> {
- // Load the link URL in a new tab.
+ // Load the link URL in a new tab and move to it.
+ addNewTab(linkUrl, true);
+
+ // Consume the event.
+ return true;
+ });
+
+ // Add an Open in Background entry.
+ menu.add(R.string.open_in_background).setOnMenuItemClickListener((MenuItem item) -> {
+ // Lod the link URL in a new tab but do not move to it.
addNewTab(linkUrl, false);
// Consume the event.
// Add an Open Image in New Tab entry.
menu.add(R.string.open_image_in_new_tab).setOnMenuItemClickListener((MenuItem item) -> {
- // Load the image in a new tab.
- addNewTab(imageUrl, false);
+ // Load the image in a new tab and move to it.
+ addNewTab(imageUrl, true);
// Consume the event.
return true;
// Add a View Image entry.
menu.add(R.string.view_image).setOnMenuItemClickListener((MenuItem item) -> {
// View the image in the current tab.
- loadUrl(imageUrl);
+ loadUrl(currentWebView, imageUrl);
// Consume the event.
return true;
});
- // Add a Download Image entry.
- menu.add(R.string.download_image).setOnMenuItemClickListener((MenuItem item) -> {
- // Check if the download should be processed by an external app.
- if (sharedPreferences.getBoolean("download_with_external_app", false)) { // 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(fragmentManager, 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.
- DialogFragment downloadImageDialogFragment = DownloadImageDialog.imageUrl(imageUrl);
-
- // Show the download image alert dialog.
- downloadImageDialogFragment.show(fragmentManager, getString(R.string.download));
- }
- }
-
- // Consume the event.
- return true;
- });
-
// Add a Copy URL entry.
menu.add(R.string.copy_url).setOnMenuItemClickListener((MenuItem item) -> {
// Save the link URL in a clip data.
return true;
});
+ menu.add(R.string.save_image).setOnMenuItemClickListener((MenuItem item) -> {
+ // Instantiate the save dialog.
+ DialogFragment saveDialogFragment = SaveDialog.saveUrl(StoragePermissionDialog.SAVE_URL, imageUrl, currentWebView.getSettings().getUserAgentString(),
+ currentWebView.getAcceptFirstPartyCookies());
+
+ // Show the save raw dialog. It must be named `save_dialog` so that the file picked can update the file name.
+ saveDialogFragment.show(getSupportFragmentManager(), getString(R.string.save_dialog));
+
+ // Consume the event.
+ return true;
+ });
+
+ menu.add(R.string.save_url).setOnMenuItemClickListener((MenuItem item) -> {
+ // Instantiate the save dialog.
+ DialogFragment saveDialogFragment = SaveDialog.saveUrl(StoragePermissionDialog.SAVE_URL, linkUrl, currentWebView.getSettings().getUserAgentString(),
+ currentWebView.getAcceptFirstPartyCookies());
+
+ // Show the save raw dialog. It must be named `save_dialog` so that the file picked can update the file name.
+ saveDialogFragment.show(getSupportFragmentManager(), getString(R.string.save_dialog));
+
+ // Consume the event.
+ return true;
+ });
+
// Add an Open with App entry.
menu.add(R.string.open_with_app).setOnMenuItemClickListener((MenuItem item) -> {
// Open the link URL with an external app.
// Get a handle for the bookmarks list view.
ListView bookmarksListView = findViewById(R.id.bookmarks_drawer_listview);
+ // Get the dialog.
+ Dialog dialog = dialogFragment.getDialog();
+
+ // Remove the incorrect lint warning below that the dialog might be null.
+ assert dialog != null;
+
// Get the views from the dialog fragment.
- EditText createBookmarkNameEditText = dialogFragment.getDialog().findViewById(R.id.create_bookmark_name_edittext);
- EditText createBookmarkUrlEditText = dialogFragment.getDialog().findViewById(R.id.create_bookmark_url_edittext);
+ EditText createBookmarkNameEditText = dialog.findViewById(R.id.create_bookmark_name_edittext);
+ EditText createBookmarkUrlEditText = dialog.findViewById(R.id.create_bookmark_url_edittext);
// Extract the strings from the edit texts.
String bookmarkNameString = createBookmarkNameEditText.getText().toString();
// Get a handle for the bookmarks list view.
ListView bookmarksListView = findViewById(R.id.bookmarks_drawer_listview);
+ // Get the dialog.
+ Dialog dialog = dialogFragment.getDialog();
+
+ // Remove the incorrect lint warning below that the dialog might be null.
+ assert dialog != null;
+
// Get handles for the views in the dialog fragment.
- EditText createFolderNameEditText = dialogFragment.getDialog().findViewById(R.id.create_folder_name_edittext);
- RadioButton defaultFolderIconRadioButton = dialogFragment.getDialog().findViewById(R.id.create_folder_default_icon_radiobutton);
- ImageView folderIconImageView = dialogFragment.getDialog().findViewById(R.id.create_folder_default_icon);
+ EditText createFolderNameEditText = dialog.findViewById(R.id.create_folder_name_edittext);
+ RadioButton defaultFolderIconRadioButton = dialog.findViewById(R.id.create_folder_default_icon_radiobutton);
+ ImageView folderIconImageView = dialog.findViewById(R.id.create_folder_default_icon);
// Get new folder name string.
String folderNameString = createFolderNameEditText.getText().toString();
@Override
public void onSaveBookmark(DialogFragment dialogFragment, int selectedBookmarkDatabaseId, Bitmap favoriteIconBitmap) {
- // Get handles for the views from `dialogFragment`.
- EditText editBookmarkNameEditText = dialogFragment.getDialog().findViewById(R.id.edit_bookmark_name_edittext);
- EditText editBookmarkUrlEditText = dialogFragment.getDialog().findViewById(R.id.edit_bookmark_url_edittext);
- RadioButton currentBookmarkIconRadioButton = dialogFragment.getDialog().findViewById(R.id.edit_bookmark_current_icon_radiobutton);
+ // Get the dialog.
+ Dialog dialog = dialogFragment.getDialog();
+
+ // Remove the incorrect lint warning below that the dialog might be null.
+ assert dialog != null;
+
+ // Get handles for the views from the dialog.
+ EditText editBookmarkNameEditText = dialog.findViewById(R.id.edit_bookmark_name_edittext);
+ EditText editBookmarkUrlEditText = dialog.findViewById(R.id.edit_bookmark_url_edittext);
+ RadioButton currentBookmarkIconRadioButton = dialog.findViewById(R.id.edit_bookmark_current_icon_radiobutton);
// Store the bookmark strings.
String bookmarkNameString = editBookmarkNameEditText.getText().toString();
@Override
public void onSaveBookmarkFolder(DialogFragment dialogFragment, int selectedFolderDatabaseId, Bitmap favoriteIconBitmap) {
+ // Get the dialog.
+ Dialog dialog = dialogFragment.getDialog();
+
+ // Remove the incorrect lint warning below that the dialog might be null.
+ assert dialog != null;
+
// Get handles for the views from `dialogFragment`.
- EditText editFolderNameEditText = dialogFragment.getDialog().findViewById(R.id.edit_folder_name_edittext);
- RadioButton currentFolderIconRadioButton = dialogFragment.getDialog().findViewById(R.id.edit_folder_current_icon_radiobutton);
- RadioButton defaultFolderIconRadioButton = dialogFragment.getDialog().findViewById(R.id.edit_folder_default_icon_radiobutton);
- ImageView defaultFolderIconImageView = dialogFragment.getDialog().findViewById(R.id.edit_folder_default_icon_imageview);
+ EditText editFolderNameEditText = dialog.findViewById(R.id.edit_folder_name_edittext);
+ RadioButton currentFolderIconRadioButton = dialog.findViewById(R.id.edit_folder_current_icon_radiobutton);
+ RadioButton defaultFolderIconRadioButton = dialog.findViewById(R.id.edit_folder_default_icon_radiobutton);
+ ImageView defaultFolderIconImageView = dialog.findViewById(R.id.edit_folder_default_icon_imageview);
// Get the new folder name.
String newFolderNameString = editFolderNameEditText.getText().toString();
bookmarksCursorAdapter.changeCursor(bookmarksCursor);
}
+ // Override `onBackPressed` to handle the navigation drawer and and the WebViews.
@Override
- public void onCloseDownloadLocationPermissionDialog(int downloadType) {
- switch (downloadType) {
- case DownloadLocationPermissionDialog.DOWNLOAD_FILE:
- // Request the WRITE_EXTERNAL_STORAGE permission with a file request code.
- ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, DOWNLOAD_FILE_REQUEST_CODE);
- break;
-
- case DownloadLocationPermissionDialog.DOWNLOAD_IMAGE:
- // Request the WRITE_EXTERNAL_STORAGE permission with an image request code.
- ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, DOWNLOAD_IMAGE_REQUEST_CODE);
- break;
- }
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
- // Get a handle for the fragment manager.
- FragmentManager fragmentManager = getSupportFragmentManager();
-
- switch (requestCode) {
- case DOWNLOAD_FILE_REQUEST_CODE:
- // Show the download file alert dialog. When the dialog closes, the correct command will be used based on the permission status.
- DialogFragment downloadFileDialogFragment = DownloadFileDialog.fromUrl(downloadUrl, downloadContentDisposition, downloadContentLength);
-
- // On API 23, displaying the fragment must be delayed or the app will crash.
- if (Build.VERSION.SDK_INT == 23) {
- new Handler().postDelayed(() -> downloadFileDialogFragment.show(fragmentManager, getString(R.string.download)), 500);
- } else {
- downloadFileDialogFragment.show(fragmentManager, getString(R.string.download));
- }
-
- // Reset the download variables.
- downloadUrl = "";
- downloadContentDisposition = "";
- downloadContentLength = 0;
- break;
-
- case DOWNLOAD_IMAGE_REQUEST_CODE:
- // Show the download image alert dialog. When the dialog closes, the correct command will be used based on the permission status.
- DialogFragment downloadImageDialogFragment = DownloadImageDialog.imageUrl(downloadImageUrl);
-
- // On API 23, displaying the fragment must be delayed or the app will crash.
- if (Build.VERSION.SDK_INT == 23) {
- new Handler().postDelayed(() -> downloadImageDialogFragment.show(fragmentManager, getString(R.string.download)), 500);
- } else {
- downloadImageDialogFragment.show(fragmentManager, getString(R.string.download));
- }
-
- // Reset the image URL variable.
- downloadImageUrl = "";
- break;
-
- case SAVE_WEBPAGE_IMAGE_REQUEST_CODE:
- // Check to see if the storage permission was granted. If the dialog was canceled the grant result will be empty.
- if ((grantResults.length > 0) && (grantResults[0] == PackageManager.PERMISSION_GRANTED)) { // The storage permission was granted.
- // Save the webpage image.
- new SaveWebpageImage(this, currentWebView).execute(saveWebsiteImageFilePath);
- } else { // The storage permission was not granted.
- // Display an error snackbar.
- Snackbar.make(currentWebView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show();
- }
-
- // Reset the save website image file path.
- saveWebsiteImageFilePath = "";
- break;
- }
- }
-
- @Override
- public void onDownloadImage(DialogFragment dialogFragment, String imageUrl) {
- // 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));
-
- // Get a handle for the cookie manager.
- CookieManager cookieManager = CookieManager.getInstance();
-
- // Pass cookies to download manager if cookies are enabled. This is required to download images from websites that require a login.
- // Code contributed 2017 Hendrik Knackstedt. Copyright assigned to Soren Stoutner <soren@stoutner.com>.
- if (cookieManager.acceptCookie()) {
- // Get the cookies for `imageUrl`.
- String cookies = cookieManager.getCookie(imageUrl);
+ public void onBackPressed() {
+ // Get a handle for the drawer layout and the tab layout.
+ DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
+ TabLayout tabLayout = findViewById(R.id.tablayout);
- // Add the cookies to `downloadRequest`. In the HTTP request header, cookies are named `Cookie`.
- downloadRequest.addRequestHeader("Cookie", cookies);
- }
+ if (drawerLayout.isDrawerVisible(GravityCompat.START)) { // The navigation drawer is open.
+ // Close the navigation drawer.
+ drawerLayout.closeDrawer(GravityCompat.START);
+ } else if (drawerLayout.isDrawerVisible(GravityCompat.END)){ // The bookmarks drawer is open.
+ if (currentBookmarksFolder.isEmpty()) { // The home folder is displayed.
+ // close the bookmarks drawer.
+ drawerLayout.closeDrawer(GravityCompat.END);
+ } else { // A subfolder is displayed.
+ // Place the former parent folder in `currentFolder`.
+ currentBookmarksFolder = bookmarksDatabaseHelper.getParentFolderName(currentBookmarksFolder);
- // Get the file name from the dialog fragment.
- EditText downloadImageNameEditText = dialogFragment.getDialog().findViewById(R.id.download_image_name);
- String imageName = downloadImageNameEditText.getText().toString();
-
- // Specify the download location.
- if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { // External write permission granted.
- // Download to the public download directory.
- downloadRequest.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, imageName);
- } else { // External write permission denied.
- // Download to the app's external download directory.
- downloadRequest.setDestinationInExternalFilesDir(this, Environment.DIRECTORY_DOWNLOADS, imageName);
+ // Load the new folder.
+ loadBookmarksFolder();
}
+ } else if (displayingFullScreenVideo) { // A full screen video is shown.
+ // Get a handle for the layouts.
+ FrameLayout rootFrameLayout = findViewById(R.id.root_framelayout);
+ RelativeLayout mainContentRelativeLayout = findViewById(R.id.main_content_relativelayout);
+ FrameLayout fullScreenVideoFrameLayout = findViewById(R.id.full_screen_video_framelayout);
- // Allow `MediaScanner` to index the download if it is a media file.
- downloadRequest.allowScanningByMediaScanner();
+ // Re-enable the screen timeout.
+ fullScreenVideoFrameLayout.setKeepScreenOn(false);
- // Add the URL as the description for the download.
- downloadRequest.setDescription(imageUrl);
+ // Unset the full screen video flag.
+ displayingFullScreenVideo = false;
- // Show the download notification after the download is completed.
- downloadRequest.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
+ // Remove all the views from the full screen video frame layout.
+ fullScreenVideoFrameLayout.removeAllViews();
- // Remove the lint warning below that `downloadManager` might be `null`.
- assert downloadManager != null;
+ // Hide the full screen video frame layout.
+ fullScreenVideoFrameLayout.setVisibility(View.GONE);
- // Initiate the download.
- downloadManager.enqueue(downloadRequest);
- } else { // The image is not an HTTP or HTTPS URI.
- Snackbar.make(currentWebView, R.string.cannot_download_image, Snackbar.LENGTH_INDEFINITE).show();
- }
- }
+ // Enable the sliding drawers.
+ drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
- @Override
- public void onDownloadFile(DialogFragment dialogFragment, String downloadUrl) {
- // Download the file if it has an HTTP or HTTPS URI.
- if (downloadUrl.startsWith("http")) {
- // Get a handle for the system `DOWNLOAD_SERVICE`.
- DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
+ // Show the main content relative layout.
+ mainContentRelativeLayout.setVisibility(View.VISIBLE);
- // Parse `downloadUrl`.
- DownloadManager.Request downloadRequest = new DownloadManager.Request(Uri.parse(downloadUrl));
+ // Apply the appropriate full screen mode flags.
+ if (fullScreenBrowsingModeEnabled && inFullScreenBrowsingMode) { // Privacy Browser is currently in full screen browsing mode.
+ // Hide the app bar if specified.
+ if (hideAppBar) {
+ // Get handles for the views.
+ LinearLayout tabsLinearLayout = findViewById(R.id.tabs_linearlayout);
+ ActionBar actionBar = getSupportActionBar();
- // Get a handle for the cookie manager.
- CookieManager cookieManager = CookieManager.getInstance();
-
- // Pass cookies to download manager if cookies are enabled. This is required to download files from websites that require a login.
- // Code contributed 2017 Hendrik Knackstedt. Copyright assigned to Soren Stoutner <soren@stoutner.com>.
- if (cookieManager.acceptCookie()) {
- // 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 the dialog fragment.
- EditText downloadFileNameEditText = dialogFragment.getDialog().findViewById(R.id.download_file_name);
- String fileName = downloadFileNameEditText.getText().toString();
-
- // Specify the download location.
- if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { // External write permission granted.
- // Download to the public download directory.
- downloadRequest.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName);
- } else { // External write permission denied.
- // Download to the app's external download directory.
- downloadRequest.setDestinationInExternalFilesDir(this, Environment.DIRECTORY_DOWNLOADS, fileName);
- }
-
- // Allow `MediaScanner` to index the download if it is a media file.
- downloadRequest.allowScanningByMediaScanner();
+ // Remove the incorrect lint warning below that the action bar might be null.
+ assert actionBar != null;
- // Add the URL as the description for the download.
- downloadRequest.setDescription(downloadUrl);
+ // Hide the tab linear layout.
+ tabsLinearLayout.setVisibility(View.GONE);
- // Show the download notification after the download is completed.
- downloadRequest.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
+ // Hide the action bar.
+ actionBar.hide();
+ }
- // Remove the lint warning below that `downloadManager` might be `null`.
- assert downloadManager != null;
+ // Hide the banner ad in the free flavor.
+ if (BuildConfig.FLAVOR.contentEquals("free")) {
+ AdHelper.hideAd(findViewById(R.id.adview));
+ }
- // Initiate the download.
- downloadManager.enqueue(downloadRequest);
- } else { // The download is not an HTTP or HTTPS URI.
- Snackbar.make(currentWebView, R.string.cannot_download_file, Snackbar.LENGTH_INDEFINITE).show();
- }
- }
+ // Remove the translucent status flag. This is necessary so the root frame layout can fill the entire screen.
+ getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
- // Override `onBackPressed` to handle the navigation drawer and and the WebViews.
- @Override
- public void onBackPressed() {
- // Get a handle for the drawer layout and the tab layout.
- DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
- TabLayout tabLayout = findViewById(R.id.tablayout);
+ /* Hide the system bars.
+ * SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen.
+ * SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN makes the root frame layout fill the area that is normally reserved for the status bar.
+ * 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 re-hides them after they are shown.
+ */
+ rootFrameLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
+ View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
+ } else { // Switch to normal viewing mode.
+ // Remove the `SYSTEM_UI` flags from the root frame layout.
+ rootFrameLayout.setSystemUiVisibility(0);
- if (drawerLayout.isDrawerVisible(GravityCompat.START)) { // The navigation drawer is open.
- // Close the navigation drawer.
- drawerLayout.closeDrawer(GravityCompat.START);
- } else if (drawerLayout.isDrawerVisible(GravityCompat.END)){ // The bookmarks drawer is open.
- if (currentBookmarksFolder.isEmpty()) { // The home folder is displayed.
- // close the bookmarks drawer.
- drawerLayout.closeDrawer(GravityCompat.END);
- } else { // A subfolder is displayed.
- // Place the former parent folder in `currentFolder`.
- currentBookmarksFolder = bookmarksDatabaseHelper.getParentFolderName(currentBookmarksFolder);
+ // Add the translucent status flag.
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+ }
- // Load the new folder.
- loadBookmarksFolder();
+ // Reload the ad for the free flavor if not in full screen mode.
+ if (BuildConfig.FLAVOR.contentEquals("free") && !inFullScreenBrowsingMode) {
+ // Reload the ad.
+ AdHelper.loadAd(findViewById(R.id.adview), getApplicationContext(), getString(R.string.ad_unit_id));
}
} else if (currentWebView.canGoBack()) { // There is at least one item in the current WebView history.
// Get the current web back forward list.
// Get the previous entry URL.
String previousUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() - 1).getUrl();
- // Reset the current domain name so that navigation works if third-party requests are blocked.
- currentWebView.resetCurrentDomainName();
-
// Apply the domain settings.
applyDomainSettings(currentWebView, previousUrl, false, false);
// Close the current tab.
closeCurrentTab();
} else { // There isn't anything to do in Privacy Browser.
- // Run the default commands.
- super.onBackPressed();
+ // Close Privacy Browser. `finishAndRemoveTask()` also removes Privacy Browser from the recent app list.
+ if (Build.VERSION.SDK_INT >= 21) {
+ finishAndRemoveTask();
+ } else {
+ finish();
+ }
// Manually kill Privacy Browser. Otherwise, it is glitchy when restarted.
System.exit(0);
// Process the results of a file browse.
@Override
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ public void onActivityResult(int requestCode, int resultCode, Intent returnedIntent) {
+ // Run the default commands.
+ super.onActivityResult(requestCode, resultCode, returnedIntent);
+
// Run the commands that correlate to the specified request code.
switch (requestCode) {
- case FILE_UPLOAD_REQUEST_CODE:
+ case BROWSE_FILE_UPLOAD_REQUEST_CODE:
// File uploads only work on API >= 21.
if (Build.VERSION.SDK_INT >= 21) {
// Pass the file to the WebView.
- fileChooserCallback.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, data));
+ fileChooserCallback.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, returnedIntent));
}
break;
- case BROWSE_SAVE_WEBPAGE_IMAGE_REQUEST_CODE:
+ case BROWSE_SAVE_WEBPAGE_REQUEST_CODE:
// Don't do anything if the user pressed back from the file picker.
if (resultCode == Activity.RESULT_OK) {
// Get a handle for the save dialog fragment.
- DialogFragment saveWebpageImageDialogFragment= (DialogFragment) getSupportFragmentManager().findFragmentByTag(getString(R.string.save_as_image));
+ DialogFragment saveWebpageDialogFragment = (DialogFragment) getSupportFragmentManager().findFragmentByTag(getString(R.string.save_dialog));
+
+ // Only update the file name if the dialog still exists.
+ if (saveWebpageDialogFragment != null) {
+ // Get a handle for the save webpage dialog.
+ Dialog saveWebpageDialog = saveWebpageDialogFragment.getDialog();
+
+ // Remove the incorrect lint warning below that the dialog might be null.
+ assert saveWebpageDialog != null;
+
+ // Get a handle for the file name edit text.
+ EditText fileNameEditText = saveWebpageDialog.findViewById(R.id.file_name_edittext);
+ TextView fileExistsWarningTextView = saveWebpageDialog.findViewById(R.id.file_exists_warning_textview);
+
+ // Instantiate the file name helper.
+ FileNameHelper fileNameHelper = new FileNameHelper();
+
+ // Get the file path if it isn't null.
+ if (returnedIntent.getData() != null) {
+ // Convert the file name URI to a file name path.
+ String fileNamePath = fileNameHelper.convertUriToFileNamePath(returnedIntent.getData());
+
+ // Set the file name path as the text of the file name edit text.
+ fileNameEditText.setText(fileNamePath);
+
+ // Move the cursor to the end of the file name edit text.
+ fileNameEditText.setSelection(fileNamePath.length());
+
+ // Hide the file exists warning.
+ fileExistsWarningTextView.setVisibility(View.GONE);
+ }
+ }
+ }
+ break;
+
+ case BROWSE_OPEN_REQUEST_CODE:
+ // Don't do anything if the user pressed back from the file picker.
+ if (resultCode == Activity.RESULT_OK) {
+ // Get a handle for the open dialog fragment.
+ DialogFragment openDialogFragment = (DialogFragment) getSupportFragmentManager().findFragmentByTag(getString(R.string.open));
// Only update the file name if the dialog still exists.
- if (saveWebpageImageDialogFragment != null) {
- // Get a handle for the save webpage image dialog.
- Dialog saveWebpageImageDialog = saveWebpageImageDialogFragment.getDialog();
+ if (openDialogFragment != null) {
+ // Get a handle for the open dialog.
+ Dialog openDialog = openDialogFragment.getDialog();
+
+ // Remove the incorrect lint warning below that the dialog might be null.
+ assert openDialog != null;
// Get a handle for the file name edit text.
- EditText fileNameEditText = saveWebpageImageDialog.findViewById(R.id.file_name_edittext);
+ EditText fileNameEditText = openDialog.findViewById(R.id.file_name_edittext);
// Instantiate the file name helper.
FileNameHelper fileNameHelper = new FileNameHelper();
- // Convert the file name URI to a file name path.
- String fileNamePath = fileNameHelper.convertUriToFileNamePath(data.getData());
+ // Get the file path if it isn't null.
+ if (returnedIntent.getData() != null) {
+ // Convert the file name URI to a file name path.
+ String fileNamePath = fileNameHelper.convertUriToFileNamePath(returnedIntent.getData());
+
+ // Set the file name path as the text of the file name edit text.
+ fileNameEditText.setText(fileNamePath);
- // Set the file name path as the text of the file name edit text.
- fileNameEditText.setText(fileNamePath);
+ // Move the cursor to the end of the file name edit text.
+ fileNameEditText.setSelection(fileNamePath.length());
+ }
}
}
break;
urlEditText.clearFocus();
// Make it so.
- loadUrl(url);
+ loadUrl(currentWebView, url);
}
- private void loadUrl(String url) {
+ private void loadUrl(NestedScrollWebView nestedScrollWebView, String url) {
// Sanitize the URL.
url = sanitizeUrl(url);
// Apply the domain settings.
- applyDomainSettings(currentWebView, url, true, false);
+ applyDomainSettings(nestedScrollWebView, url, true, false);
// Load the URL.
- currentWebView.loadUrl(url, customHeaders);
+ nestedScrollWebView.loadUrl(url, customHeaders);
}
public void findPreviousOnPage(View view) {
}
@Override
- public void onSaveWebpageImage(DialogFragment dialogFragment) {
+ public void onApplyNewFontSize(DialogFragment dialogFragment) {
+ // Get the dialog.
+ Dialog dialog = dialogFragment.getDialog();
+
+ // Remove the incorrect lint warning below tha the dialog might be null.
+ assert dialog != null;
+
+ // Get a handle for the font size edit text.
+ EditText fontSizeEditText = dialog.findViewById(R.id.font_size_edittext);
+
+ // Initialize the new font size variable with the current font size.
+ int newFontSize = currentWebView.getSettings().getTextZoom();
+
+ // Get the font size from the edit text.
+ try {
+ newFontSize = Integer.parseInt(fontSizeEditText.getText().toString());
+ } catch (Exception exception) {
+ // If the edit text does not contain a valid font size do nothing.
+ }
+
+ // Apply the new font size.
+ currentWebView.getSettings().setTextZoom(newFontSize);
+ }
+
+ @Override
+ public void onOpen(DialogFragment dialogFragment) {
+ // Get the dialog.
+ Dialog dialog = dialogFragment.getDialog();
+
+ // Remove the incorrect lint warning below that the dialog might be null.
+ assert dialog != null;
+
// Get a handle for the file name edit text.
- EditText fileNameEditText = dialogFragment.getDialog().findViewById(R.id.file_name_edittext);
+ EditText fileNameEditText = dialog.findViewById(R.id.file_name_edittext);
// Get the file path string.
- saveWebsiteImageFilePath = fileNameEditText.getText().toString();
+ openFilePath = fileNameEditText.getText().toString();
+
+ // Check to see if the storage permission is needed.
+ if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { // The storage permission has been granted.
+ // Open the file.
+ currentWebView.loadUrl("file://" + openFilePath);
+ } else { // The storage permission has not been granted.
+ // Get the external private directory file.
+ File externalPrivateDirectoryFile = getExternalFilesDir(null);
+
+ // Remove the incorrect lint error below that the file might be null.
+ assert externalPrivateDirectoryFile != null;
+
+ // Get the external private directory string.
+ String externalPrivateDirectory = externalPrivateDirectoryFile.toString();
+
+ // Check to see if the file path is in the external private directory.
+ if (openFilePath.startsWith(externalPrivateDirectory)) { // the file path is in the external private directory.
+ // Open the file.
+ currentWebView.loadUrl("file://" + openFilePath);
+ } else { // The file path is in a public directory.
+ // Check if the user has previously denied the storage permission.
+ if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { // Show a dialog explaining the request first.
+ // Instantiate the storage permission alert dialog.
+ DialogFragment storagePermissionDialogFragment = StoragePermissionDialog.displayDialog(StoragePermissionDialog.OPEN);
+
+ // Show the storage permission alert dialog. The permission will be requested the the dialog is closed.
+ storagePermissionDialogFragment.show(getSupportFragmentManager(), getString(R.string.storage_permission));
+ } else { // Show the permission request directly.
+ // Request the write external storage permission. The file will be opened when it finishes.
+ ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_OPEN_REQUEST_CODE);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onSaveWebpage(int saveType, DialogFragment dialogFragment) {
+ // Get the dialog.
+ Dialog dialog = dialogFragment.getDialog();
+
+ // Remove the incorrect lint warning below that the dialog might be null.
+ assert dialog != null;
+
+ // Get a handle for the edit texts.
+ EditText urlEditText = dialog.findViewById(R.id.url_edittext);
+ EditText fileNameEditText = dialog.findViewById(R.id.file_name_edittext);
+
+ // Get the strings from the edit texts.
+ saveWebpageUrl = urlEditText.getText().toString();
+ saveWebpageFilePath = fileNameEditText.getText().toString();
// Check to see if the storage permission is needed.
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { // The storage permission has been granted.
- // Save the webpage image.
- new SaveWebpageImage(this, currentWebView).execute(saveWebsiteImageFilePath);
+ //Save the webpage according to the save type.
+ switch (saveType) {
+ case StoragePermissionDialog.SAVE_URL:
+ // Save the URL.
+ new SaveUrl(this, this, saveWebpageFilePath, currentWebView.getSettings().getUserAgentString(), currentWebView.getAcceptFirstPartyCookies()).execute(saveWebpageUrl);
+ break;
+
+ case StoragePermissionDialog.SAVE_AS_ARCHIVE:
+ // Save the webpage archive.
+ currentWebView.saveWebArchive(saveWebpageFilePath);
+ break;
+
+ case StoragePermissionDialog.SAVE_AS_IMAGE:
+ // Save the webpage image.
+ new SaveWebpageImage(this, currentWebView).execute(saveWebpageFilePath);
+ break;
+ }
} else { // The storage permission has not been granted.
- // Get the external private directory `File`.
+ // Get the external private directory file.
File externalPrivateDirectoryFile = getExternalFilesDir(null);
// Remove the incorrect lint error below that the file might be null.
String externalPrivateDirectory = externalPrivateDirectoryFile.toString();
// Check to see if the file path is in the external private directory.
- if (saveWebsiteImageFilePath.startsWith(externalPrivateDirectory)) { // The file path is in the external private directory.
- // Save the webpage image.
- new SaveWebpageImage(this, currentWebView).execute(saveWebsiteImageFilePath);
+ if (saveWebpageFilePath.startsWith(externalPrivateDirectory)) { // The file path is in the external private directory.
+ // Save the webpage according to the save type.
+ switch (saveType) {
+ case StoragePermissionDialog.SAVE_URL:
+ // Save the URL.
+ new SaveUrl(this, this, saveWebpageFilePath, currentWebView.getSettings().getUserAgentString(), currentWebView.getAcceptFirstPartyCookies()).execute(saveWebpageUrl);
+ break;
+
+ case StoragePermissionDialog.SAVE_AS_ARCHIVE:
+ // Save the webpage archive.
+ currentWebView.saveWebArchive(saveWebpageFilePath);
+ break;
+
+ case StoragePermissionDialog.SAVE_AS_IMAGE:
+ // Save the webpage image.
+ new SaveWebpageImage(this, currentWebView).execute(saveWebpageFilePath);
+ break;
+ }
} else { // The file path is in a public directory.
// Check if the user has previously denied the storage permission.
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { // Show a dialog explaining the request first.
// Instantiate the storage permission alert dialog.
- DialogFragment storagePermissionDialogFragment = new StoragePermissionDialog();
+ DialogFragment storagePermissionDialogFragment = StoragePermissionDialog.displayDialog(saveType);
// Show the storage permission alert dialog. The permission will be requested when the dialog is closed.
storagePermissionDialogFragment.show(getSupportFragmentManager(), getString(R.string.storage_permission));
} else { // Show the permission request directly.
- // Request the write external storage permission. The webpage image will be saved when it finishes.
- ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, SAVE_WEBPAGE_IMAGE_REQUEST_CODE);
+ switch (saveType) {
+ case StoragePermissionDialog.SAVE_URL:
+ // Request the write external storage permission. The URL will be saved when it finishes.
+ ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_SAVE_URL_REQUEST_CODE);
+
+ case StoragePermissionDialog.SAVE_AS_ARCHIVE:
+ // Request the write external storage permission. The webpage archive will be saved when it finishes.
+ ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_SAVE_AS_ARCHIVE_REQUEST_CODE);
+ break;
+
+ case StoragePermissionDialog.SAVE_AS_IMAGE:
+ // Request the write external storage permission. The webpage image will be saved when it finishes.
+ ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_SAVE_AS_IMAGE_REQUEST_CODE);
+ break;
+ }
}
}
}
}
@Override
- public void onCloseStoragePermissionDialog() {
- // Request the write external storage permission. The webpage image will be saved when it finishes.
- ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, SAVE_WEBPAGE_IMAGE_REQUEST_CODE);
+ public void onCloseStoragePermissionDialog(int requestType) {
+ switch (requestType) {
+ case StoragePermissionDialog.OPEN:
+ // Request the write external storage permission. The file will be opened when it finishes.
+ ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_OPEN_REQUEST_CODE);
+ break;
+
+ case StoragePermissionDialog.SAVE_URL:
+ // Request the write external storage permission. The URL will be saved when it finishes.
+ ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_SAVE_URL_REQUEST_CODE);
+ break;
+
+ case StoragePermissionDialog.SAVE_AS_ARCHIVE:
+ // Request the write external storage permission. The webpage archive will be saved when it finishes.
+ ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_SAVE_AS_ARCHIVE_REQUEST_CODE);
+ break;
+
+ case StoragePermissionDialog.SAVE_AS_IMAGE:
+ // Request the write external storage permission. The webpage image will be saved when it finishes.
+ ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_SAVE_AS_IMAGE_REQUEST_CODE);
+ break;
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ //Only process the results if they exist (this method is triggered when a dialog is presented the first time for an app, but no grant results are included).
+ if (grantResults.length > 0) {
+ switch (requestCode) {
+ case PERMISSION_OPEN_REQUEST_CODE:
+ // Check to see if the storage permission was granted. If the dialog was canceled the grant results will be empty.
+ if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { // The storage permission was granted.
+ // Load the file.
+ currentWebView.loadUrl("file://" + openFilePath);
+ } else { // The storage permission was not granted.
+ // Display an error snackbar.
+ Snackbar.make(currentWebView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show();
+ }
+
+ // Reset the open file path.
+ openFilePath = "";
+ break;
+
+ case PERMISSION_SAVE_URL_REQUEST_CODE:
+ // Check to see if the storage permission was granted. If the dialog was canceled the grant results will be empty.
+ if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { // The storage permission was granted.
+ // Save the raw URL.
+ new SaveUrl(this, this, saveWebpageFilePath, currentWebView.getSettings().getUserAgentString(), currentWebView.getAcceptFirstPartyCookies()).execute(saveWebpageUrl);
+ } else { // The storage permission was not granted.
+ // Display an error snackbar.
+ Snackbar.make(currentWebView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show();
+ }
+
+ // Reset the save strings.
+ saveWebpageUrl = "";
+ saveWebpageFilePath = "";
+ break;
+
+ case PERMISSION_SAVE_AS_ARCHIVE_REQUEST_CODE:
+ // Check to see if the storage permission was granted. If the dialog was canceled the grant results will be empty.
+ if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { // The storage permission was granted.
+ // Save the webpage archive.
+ currentWebView.saveWebArchive(saveWebpageFilePath);
+ } else { // The storage permission was not granted.
+ // Display an error snackbar.
+ Snackbar.make(currentWebView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show();
+ }
+
+ // Reset the save webpage file path.
+ saveWebpageFilePath = "";
+ break;
+
+ case PERMISSION_SAVE_AS_IMAGE_REQUEST_CODE:
+ // Check to see if the storage permission was granted. If the dialog was canceled the grant results will be empty.
+ if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { // The storage permission was granted.
+ // Save the webpage image.
+ new SaveWebpageImage(this, currentWebView).execute(saveWebpageFilePath);
+ } else { // The storage permission was not granted.
+ // Display an error snackbar.
+ Snackbar.make(currentWebView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show();
+ }
+
+ // Reset the save webpage file path.
+ saveWebpageFilePath = "";
+ break;
+ }
+ }
}
private void applyAppSettings() {
sanitizeGoogleAnalytics = sharedPreferences.getBoolean("google_analytics", true);
sanitizeFacebookClickIds = sharedPreferences.getBoolean("facebook_click_ids", true);
sanitizeTwitterAmpRedirects = sharedPreferences.getBoolean("twitter_amp_redirects", true);
- proxyThroughOrbot = sharedPreferences.getBoolean("proxy_through_orbot", false);
+ proxyMode = sharedPreferences.getString("proxy", getString(R.string.proxy_default_value));
fullScreenBrowsingModeEnabled = sharedPreferences.getBoolean("full_screen_browsing_mode", false);
hideAppBar = sharedPreferences.getBoolean("hide_app_bar", true);
scrollAppBar = sharedPreferences.getBoolean("scroll_app_bar", true);
+ // Get the search string.
+ String searchString = sharedPreferences.getString("search", getString(R.string.search_default_value));
+
+ // Set the search string.
+ if (searchString.equals("Custom URL")) { // A custom search string is used.
+ searchURL = sharedPreferences.getString("search_custom_url", getString(R.string.search_custom_url_default_value));
+ } else { // A custom search string is not used.
+ searchURL = searchString;
+ }
+
// Get handles for the views that need to be modified.
FrameLayout rootFrameLayout = findViewById(R.id.root_framelayout);
AppBarLayout appBarLayout = findViewById(R.id.appbar_layout);
// Remove the incorrect lint warning below that the action bar might be null.
assert actionBar != null;
- // Apply the proxy through Orbot settings.
- applyProxyThroughOrbot(false);
+ // Apply the proxy.
+ applyProxy(false);
// Set Do Not Track status.
if (doNotTrackEnabled) {
}
});
- // Initialize the Orbot status and the waiting for Orbot trackers.
- orbotStatus = "unknown";
- waitingForOrbot = false;
-
- // Create an Orbot status `BroadcastReceiver`.
+ // Create an Orbot status broadcast receiver.
orbotStatusBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// Store the content of the status message in `orbotStatus`.
orbotStatus = intent.getStringExtra("org.torproject.android.intent.extra.STATUS");
- // If Privacy Browser is waiting on Orbot, load the website now that Orbot is connected.
- if (orbotStatus.equals("ON") && waitingForOrbot) {
- // Reset the waiting for Orbot status.
- waitingForOrbot = false;
+ // If Privacy Browser is waiting on the proxy, load the website now that Orbot is connected.
+ if ((orbotStatus != null) && orbotStatus.equals("ON") && waitingForProxy) {
+ // Reset the waiting for proxy status.
+ waitingForProxy = false;
+
+ // Get a handle for the waiting for proxy dialog.
+ DialogFragment waitingForProxyDialogFragment = (DialogFragment) getSupportFragmentManager().findFragmentByTag(getString(R.string.waiting_for_proxy_dialog));
- // Get the intent that started the app.
- Intent launchingIntent = getIntent();
+ // Dismiss the waiting for proxy dialog if it is displayed.
+ if (waitingForProxyDialogFragment != null) {
+ waitingForProxyDialogFragment.dismiss();
+ }
- // Get the information from the intent.
- String launchingIntentAction = launchingIntent.getAction();
- Uri launchingIntentUriData = launchingIntent.getData();
+ // Reload existing URLs and load any URLs that are waiting for the proxy.
+ for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
+ // Get the WebView tab fragment.
+ WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i);
- // 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;
+ // Get the fragment view.
+ View fragmentView = webViewTabFragment.getView();
- // Sanitize the search input and convert it to a search.
- try {
- encodedUrlString = URLEncoder.encode(launchingIntent.getStringExtra(SearchManager.QUERY), "UTF-8");
- } catch (UnsupportedEncodingException exception) {
- encodedUrlString = "";
- }
+ // Only process the WebViews if they exist.
+ if (fragmentView != null) {
+ // Get the nested scroll WebView from the tab fragment.
+ NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
- // Load the completed search URL.
- loadUrl(searchURL + encodedUrlString);
- } else if (launchingIntentUriData != null){ // Check to see if the intent contains a new URL.
- // Load the URL from the intent.
- loadUrl(launchingIntentUriData.toString());
- } else { // The is no URL in the intent.
- // Select the homepage based on the proxy through Orbot status.
- if (proxyThroughOrbot) {
- // Load the Tor homepage.
- loadUrl(sharedPreferences.getString("tor_homepage", getString(R.string.tor_homepage_default_value)));
- } else {
- // Load the normal homepage.
- loadUrl(sharedPreferences.getString("homepage", getString(R.string.homepage_default_value)));
+ // Get the waiting for proxy URL string.
+ String waitingForProxyUrlString = nestedScrollWebView.getWaitingForProxyUrlString();
+
+ // Load the pending URL if it exists.
+ if (!waitingForProxyUrlString.isEmpty()) { // A URL is waiting to be loaded.
+ // Load the URL.
+ loadUrl(nestedScrollWebView, waitingForProxyUrlString);
+
+ // Reset the waiting for proxy URL string.
+ nestedScrollWebView.resetWaitingForProxyUrlString();
+ } else { // No URL is waiting to be loaded.
+ // Reload the existing URL.
+ nestedScrollWebView.reload();
+ }
}
}
}
}
};
- // Register `orbotStatusBroadcastReceiver` on `this` context.
+ // Register the Orbot status broadcast receiver on `this` context.
this.registerReceiver(orbotStatusBroadcastReceiver, new IntentFilter("org.torproject.android.intent.action.STATUS"));
// Get handles for views that need to be modified.
// Listen for touches on the navigation menu.
navigationView.setNavigationItemSelectedListener(this);
- // Get handles for the navigation menu and the back and forward menu items. The menu is zero-based.
+ // Get handles for the navigation menu and the back and forward menu items. The menu is 0 based.
Menu navigationMenu = navigationView.getMenu();
MenuItem navigationBackMenuItem = navigationMenu.getItem(2);
MenuItem navigationForwardMenuItem = navigationMenu.getItem(3);
MenuItem navigationHistoryMenuItem = navigationMenu.getItem(4);
- MenuItem navigationRequestsMenuItem = navigationMenu.getItem(5);
+ MenuItem navigationRequestsMenuItem = navigationMenu.getItem(6);
// Update the web view pager every time a tab is modified.
webViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
bookmarksListView.setOnItemClickListener((parent, view, position, id) -> {
// Convert the id from long to int to match the format of the bookmarks database.
- int databaseID = (int) id;
+ int databaseId = (int) id;
- // Get the bookmark cursor for this ID and move it to the first row.
- Cursor bookmarkCursor = bookmarksDatabaseHelper.getBookmark(databaseID);
+ // Get the bookmark cursor for this ID.
+ Cursor bookmarkCursor = bookmarksDatabaseHelper.getBookmark(databaseId);
+
+ // Move the bookmark cursor to the first row.
bookmarkCursor.moveToFirst();
// Act upon the bookmark according to the type.
loadBookmarksFolder();
} else { // The selected bookmark is not a folder.
// Load the bookmark URL.
- loadUrl(bookmarkCursor.getString(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_URL)));
+ loadUrl(currentWebView, bookmarkCursor.getString(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_URL)));
// Close the bookmarks drawer.
drawerLayout.closeDrawer(GravityCompat.END);
// Save the current folder name, which is used in `onSaveEditBookmarkFolder()`.
oldFolderNameString = bookmarksCursor.getString(bookmarksCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME));
- // Show the edit bookmark folder `AlertDialog` and name the instance `@string/edit_folder`.
+ // Instantiate the edit folder bookmark dialog.
DialogFragment editBookmarkFolderDialog = EditBookmarkFolderDialog.folderDatabaseId(databaseId, currentWebView.getFavoriteOrDefaultIcon());
+
+ // Show the edit folder bookmark dialog.
editBookmarkFolderDialog.show(getSupportFragmentManager(), getString(R.string.edit_folder));
} else {
- // Show the edit bookmark `AlertDialog` and name the instance `@string/edit_bookmark`.
- DialogFragment editBookmarkDialog = EditBookmarkDialog.bookmarkDatabaseId(databaseId, currentWebView.getFavoriteOrDefaultIcon());
- editBookmarkDialog.show(getSupportFragmentManager(), getString(R.string.edit_bookmark));
+ // Get the bookmark cursor for this ID.
+ Cursor bookmarkCursor = bookmarksDatabaseHelper.getBookmark(databaseId);
+
+ // Move the bookmark cursor to the first row.
+ bookmarkCursor.moveToFirst();
+
+ // Load the bookmark in a new tab but do not switch to the tab or close the drawer.
+ addNewTab(bookmarkCursor.getString(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_URL)), false);
}
// Consume the event.
drawerHeaderPaddingTop = statusBarPixelSize + (int) (4 * screenDensity);
drawerHeaderPaddingBottom = (int) (8 * screenDensity);
- // The drawer listener is used to update the navigation menu.`
+ // The drawer listener is used to update the navigation menu.
drawerLayout.addDrawerListener(new DrawerLayout.DrawerListener() {
@Override
public void onDrawerSlide(@NonNull View drawerView, float slideOffset) {
@Override
public void navigateHistory(String url, int steps) {
- // Reset the current domain name so that navigation works if third-party requests are blocked.
- currentWebView.resetCurrentDomainName();
-
// Apply the domain settings.
applyDomainSettings(currentWebView, url, false, false);
// Get the previous entry URL.
String previousUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() - 1).getUrl();
- // Reset the current domain name so that navigation works if third-party requests are blocked.
- currentWebView.resetCurrentDomainName();
-
// Apply the domain settings.
applyDomainSettings(currentWebView, previousUrl, false, false);
}
// Apply the font size.
- if (fontSize == 0) { // Apply the default font size.
- nestedScrollWebView.getSettings().setTextZoom(Integer.valueOf(defaultFontSizeString));
- } else { // Apply the specified font size.
- nestedScrollWebView.getSettings().setTextZoom(fontSize);
+ try { // Try the specified font size to see if it is valid.
+ if (fontSize == 0) { // Apply the default font size.
+ // Try to set the font size from the value in the app settings.
+ nestedScrollWebView.getSettings().setTextZoom(Integer.parseInt(defaultFontSizeString));
+ } else { // Apply the font size from domain settings.
+ nestedScrollWebView.getSettings().setTextZoom(fontSize);
+ }
+ } catch (Exception exception) { // The specified font size is invalid
+ // Set the font size to be 100%
+ nestedScrollWebView.getSettings().setTextZoom(100);
}
// Set the user agent.
nestedScrollWebView.getSettings().setJavaScriptEnabled(defaultJavaScriptEnabled);
}
- // Apply the default settings.
+ // Apply the default first-party cookie setting.
cookieManager.setAcceptCookie(nestedScrollWebView.getAcceptFirstPartyCookies());
- nestedScrollWebView.getSettings().setTextZoom(Integer.valueOf(defaultFontSizeString));
+
+ // Apply the default font size setting.
+ try {
+ // Try to set the font size from the value in the app settings.
+ nestedScrollWebView.getSettings().setTextZoom(Integer.parseInt(defaultFontSizeString));
+ } catch (Exception exception) {
+ // If the app settings value is invalid, set the font size to 100%.
+ nestedScrollWebView.getSettings().setTextZoom(100);
+ }
// Apply the form data setting if the API < 26.
if (Build.VERSION.SDK_INT < 26) {
return !nestedScrollWebView.getSettings().getUserAgentString().equals(initialUserAgent);
}
- private void applyProxyThroughOrbot(boolean reloadWebsite) {
+ private void applyProxy(boolean reloadWebViews) {
// Get a handle for the shared preferences.
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
- // Get the search and theme preferences.
- 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));
+ // Get the theme preferences.
boolean darkTheme = sharedPreferences.getBoolean("dark_theme", false);
// Get a handle for the app bar layout.
AppBarLayout appBarLayout = findViewById(R.id.appbar_layout);
- // Set the homepage, search, and proxy options.
- if (proxyThroughOrbot) { // Set the Tor options.
- // 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 according to the mode. `this` refers to the current activity where an alert dialog might be displayed.
+ ProxyHelper.setProxy(getApplicationContext(), appBarLayout, proxyMode);
- // Set the proxy. `this` refers to the current activity where an `AlertDialog` might be displayed.
- OrbotProxyHelper.setProxy(getApplicationContext(), this, "localhost", "8118");
+ // Reset the waiting for proxy tracker.
+ waitingForProxy = false;
- // Set the app bar background to indicate proxying through Orbot is enabled.
- if (darkTheme) {
- appBarLayout.setBackgroundResource(R.color.dark_blue_30);
- } else {
- appBarLayout.setBackgroundResource(R.color.blue_50);
- }
+ // Update the user interface and reload the WebViews if requested.
+ switch (proxyMode) {
+ case ProxyHelper.NONE:
+ // Set the default app bar layout background.
+ if (darkTheme) {
+ appBarLayout.setBackgroundResource(R.color.gray_900);
+ } else {
+ appBarLayout.setBackgroundResource(R.color.gray_100);
+ }
+ break;
- // Check to see if Orbot is ready.
- if (!orbotStatus.equals("ON")) { // Orbot is not ready.
- // Set `waitingForOrbot`.
- waitingForOrbot = true;
+ case ProxyHelper.TOR:
+ // Set the app bar background to indicate proxying through Orbot is enabled.
+ if (darkTheme) {
+ appBarLayout.setBackgroundResource(R.color.dark_blue_30);
+ } else {
+ appBarLayout.setBackgroundResource(R.color.blue_50);
+ }
- // Disable the wide view port so that the waiting for Orbot text is displayed correctly.
- currentWebView.getSettings().setUseWideViewPort(false);
+ // Check to see if Orbot is installed.
+ try {
+ // Get the package manager.
+ PackageManager packageManager = getPackageManager();
- // Load a waiting page. `null` specifies no encoding, which defaults to ASCII.
- currentWebView.loadData("<html><body><br/><center><h1>" + getString(R.string.waiting_for_orbot) + "</h1></center></body></html>", "text/html", null);
- } else if (reloadWebsite) { // Orbot is ready and the website should be reloaded.
- // Reload the website.
- currentWebView.reload();
- }
- } else { // Set the non-Tor options.
- // 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;
- }
+ // Check to see if Orbot is in the list. This will throw an error and drop to the catch section if it isn't installed.
+ packageManager.getPackageInfo("org.torproject.android", 0);
- // Reset the proxy to default. The host is `""` and the port is `"0"`.
- OrbotProxyHelper.setProxy(getApplicationContext(), this, "", "0");
+ // Check to see if the proxy is ready.
+ if (!orbotStatus.equals("ON")) { // Orbot is not ready.
+ // Set the waiting for proxy status.
+ waitingForProxy = true;
- // Set the default app bar layout background.
- if (darkTheme) {
- appBarLayout.setBackgroundResource(R.color.gray_900);
- } else {
- appBarLayout.setBackgroundResource(R.color.gray_100);
- }
+ // Show the waiting for proxy dialog if it isn't already displayed.
+ if (getSupportFragmentManager().findFragmentByTag(getString(R.string.waiting_for_proxy_dialog)) == null) {
+ // Get a handle for the waiting for proxy alert dialog.
+ DialogFragment waitingForProxyDialogFragment = new WaitingForProxyDialog();
- // Reset `waitingForOrbot.
- waitingForOrbot = false;
+ // Display the waiting for proxy alert dialog.
+ waitingForProxyDialogFragment.show(getSupportFragmentManager(), getString(R.string.waiting_for_proxy_dialog));
+ }
+ }
+ } catch (PackageManager.NameNotFoundException exception) { // Orbot is not installed.
+ // Show the Orbot not installed dialog if it is not already displayed.
+ if (getSupportFragmentManager().findFragmentByTag(getString(R.string.proxy_not_installed_dialog)) == null) {
+ // Get a handle for the Orbot not installed alert dialog.
+ DialogFragment orbotNotInstalledDialogFragment = ProxyNotInstalledDialog.displayDialog(proxyMode);
+
+ // Display the Orbot not installed alert dialog.
+ orbotNotInstalledDialogFragment.show(getSupportFragmentManager(), getString(R.string.proxy_not_installed_dialog));
+ }
+ }
+ break;
- // Reload the WebViews if requested.
- if (reloadWebsite) {
- // Reload the WebViews.
- for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
- // Get the WebView tab fragment.
- WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i);
+ case ProxyHelper.I2P:
+ // Set the app bar background to indicate proxying through Orbot is enabled.
+ if (darkTheme) {
+ appBarLayout.setBackgroundResource(R.color.dark_blue_30);
+ } else {
+ appBarLayout.setBackgroundResource(R.color.blue_50);
+ }
- // Get the fragment view.
- View fragmentView = webViewTabFragment.getView();
+ // Check to see if I2P is installed.
+ try {
+ // Get the package manager.
+ PackageManager packageManager = getPackageManager();
+
+ // Check to see if I2P is in the list. This will throw an error and drop to the catch section if it isn't installed.
+ packageManager.getPackageInfo("org.torproject.android", 0);
+ } catch (PackageManager.NameNotFoundException exception) { // I2P is not installed.
+ // Sow the I2P not installed dialog if it is not already displayed.
+ if (getSupportFragmentManager().findFragmentByTag(getString(R.string.proxy_not_installed_dialog)) == null) {
+ // Get a handle for the waiting for proxy alert dialog.
+ DialogFragment i2pNotInstalledDialogFragment = ProxyNotInstalledDialog.displayDialog(proxyMode);
+
+ // Display the I2P not installed alert dialog.
+ i2pNotInstalledDialogFragment.show(getSupportFragmentManager(), getString(R.string.proxy_not_installed_dialog));
+ }
+ }
+ break;
- // Only reload the WebViews if they exist.
- if (fragmentView != null) {
- // Get the nested scroll WebView from the tab fragment.
- NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
+ case ProxyHelper.CUSTOM:
+ // Set the app bar background to indicate proxying through Orbot is enabled.
+ if (darkTheme) {
+ appBarLayout.setBackgroundResource(R.color.dark_blue_30);
+ } else {
+ appBarLayout.setBackgroundResource(R.color.blue_50);
+ }
+ break;
+ }
- // Reload the WebView.
- nestedScrollWebView.reload();
- }
+ // Reload the WebViews if requested and not waiting for the proxy.
+ if (reloadWebViews && !waitingForProxy) {
+ // Reload the WebViews.
+ for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
+ // Get the WebView tab fragment.
+ WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i);
+
+ // Get the fragment view.
+ View fragmentView = webViewTabFragment.getView();
+
+ // Only reload the WebViews if they exist.
+ if (fragmentView != null) {
+ // Get the nested scroll WebView from the tab fragment.
+ NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
+
+ // Reload the WebView.
+ nestedScrollWebView.reload();
}
}
}
}
}
- 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. Specifying `text/html` displays a good 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 a handle for the URL edit text.
EditText urlEditText = findViewById(R.id.url_edittext);
String urlString = urlEditText.getText().toString();
// Highlight the URL according to the protocol.
- if (urlString.startsWith("file://")) { // This is a file URL.
- // De-emphasize only the protocol.
- urlEditText.getText().setSpan(initialGrayColorSpan, 0, 7, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
- } else if (urlString.startsWith("content://")) {
- // De-emphasize only the protocol.
- urlEditText.getText().setSpan(initialGrayColorSpan, 0, 10, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+ if (urlString.startsWith("file://") || urlString.startsWith("content://")) { // This is a file or content URL.
+ // De-emphasize everything before the file name.
+ urlEditText.getText().setSpan(initialGrayColorSpan, 0, urlString.lastIndexOf("/") + 1,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));
}
private void openWithApp(String url) {
- // Create the open with intent with `ACTION_VIEW`.
+ // Create an open with app intent with `ACTION_VIEW`.
Intent openWithAppIntent = new Intent(Intent.ACTION_VIEW);
// Set the URI but not the MIME type. This should open all available apps.
// Flag the intent to open in a new task.
openWithAppIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ // Try the intent.
try {
// Show the chooser.
startActivity(openWithAppIntent);
- } catch (ActivityNotFoundException exception) {
+ } catch (ActivityNotFoundException exception) { // There are no apps available to open the URL.
// Show a snackbar with the error.
Snackbar.make(currentWebView, getString(R.string.error) + " " + exception, Snackbar.LENGTH_INDEFINITE).show();
}
}
private void openWithBrowser(String url) {
- // Create the open with intent with `ACTION_VIEW`.
+ // Create an open with browser intent with `ACTION_VIEW`.
Intent openWithBrowserIntent = new Intent(Intent.ACTION_VIEW);
// Set the URI and the MIME type. `"text/html"` should load browser options.
// Flag the intent to open in a new task.
openWithBrowserIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ // Try the intent.
try {
// Show the chooser.
startActivity(openWithBrowserIntent);
- } catch (ActivityNotFoundException exception) {
+ } catch (ActivityNotFoundException exception) { // There are no browsers available to open the URL.
// Show a snackbar with the error.
Snackbar.make(currentWebView, getString(R.string.error) + " " + exception, Snackbar.LENGTH_INDEFINITE).show();
}
// Allow the downloading of files.
nestedScrollWebView.setDownloadListener((String downloadUrl, String userAgent, String contentDisposition, String mimetype, long contentLength) -> {
- // Check if the download should be processed by an external app.
- if (sharedPreferences.getBoolean("download_with_external_app", false)) { // Download with an external app.
- // 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. Specifying `text/html` displays a good number of options.
- downloadIntent.setDataAndType(Uri.parse(downloadUrl), "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)));
- } 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()`.
- this.downloadUrl = downloadUrl;
- 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(getSupportFragmentManager(), 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.
- DialogFragment downloadFileDialogFragment = DownloadFileDialog.fromUrl(downloadUrl, contentDisposition, contentLength);
+ // Instantiate the save dialog.
+ DialogFragment saveDialogFragment = SaveDialog.saveUrl(StoragePermissionDialog.SAVE_URL, downloadUrl, userAgent, nestedScrollWebView.getAcceptFirstPartyCookies());
- // Show the download file alert dialog.
- downloadFileDialogFragment.show(getSupportFragmentManager(), getString(R.string.download));
- }
- }
+ // Show the save dialog. It must be named `save_dialog` so that the file picker can update the file name.
+ saveDialogFragment.show(getSupportFragmentManager(), getString(R.string.save_dialog));
});
// Update the find on page count.
// Show the full screen video frame layout.
fullScreenVideoFrameLayout.setVisibility(View.VISIBLE);
+
+ // Disable the screen timeout while the video is playing. YouTube does this automatically, but not all other videos do.
+ fullScreenVideoFrameLayout.setKeepScreenOn(true);
}
// Exit full screen video.
// Get a handle for the full screen video frame layout.
FrameLayout fullScreenVideoFrameLayout = findViewById(R.id.full_screen_video_framelayout);
+ // Re-enable the screen timeout.
+ fullScreenVideoFrameLayout.setKeepScreenOn(false);
+
// Unset the full screen video flag.
displayingFullScreenVideo = false;
Intent fileChooserIntent = fileChooserParams.createIntent();
// Open the file chooser.
- startActivityForResult(fileChooserIntent, FILE_UPLOAD_REQUEST_CODE);
+ startActivityForResult(fileChooserIntent, BROWSE_FILE_UPLOAD_REQUEST_CODE);
}
return true;
}
Menu navigationMenu = navigationView.getMenu();
// Get a handle for the navigation requests menu item. The menu is 0 based.
- MenuItem navigationRequestsMenuItem = navigationMenu.getItem(5);
+ MenuItem navigationRequestsMenuItem = navigationMenu.getItem(6);
// Create an empty web resource response to be used if the resource request is blocked.
WebResourceResponse emptyWebResourceResponse = new WebResourceResponse("text/plain", "utf8", new ByteArrayInputStream("".getBytes()));
// Hide the keyboard.
inputMethodManager.hideSoftInputFromWindow(nestedScrollWebView.getWindowToken(), 0);
- // Check to see if Privacy Browser is waiting on Orbot.
- if (!waitingForOrbot) { // Process the URL.
- // Get the current page position.
- int currentPagePosition = webViewPagerAdapter.getPositionForId(nestedScrollWebView.getWebViewFragmentId());
+ // Get the current page position.
+ int currentPagePosition = webViewPagerAdapter.getPositionForId(nestedScrollWebView.getWebViewFragmentId());
- // Update the URL text bar if the page is currently selected.
- if (tabLayout.getSelectedTabPosition() == currentPagePosition) {
- // Clear the focus from the URL edit text.
- urlEditText.clearFocus();
+ // Update the URL text bar if the page is currently selected.
+ if (tabLayout.getSelectedTabPosition() == currentPagePosition) {
+ // Clear the focus from the URL edit text.
+ urlEditText.clearFocus();
- // Display the formatted URL text.
- urlEditText.setText(url);
+ // Display the formatted URL text.
+ urlEditText.setText(url);
- // Apply text highlighting to `urlTextBox`.
- highlightUrlText();
- }
+ // Apply text highlighting to `urlTextBox`.
+ highlightUrlText();
+ }
- // Reset the list of host IP addresses.
- nestedScrollWebView.clearCurrentIpAddresses();
+ // Reset the list of host IP addresses.
+ nestedScrollWebView.clearCurrentIpAddresses();
- // Get a URI for the current URL.
- Uri currentUri = Uri.parse(url);
+ // Get a URI for the current URL.
+ Uri currentUri = Uri.parse(url);
- // Get the IP addresses for the host.
- new GetHostIpAddresses(activity, getSupportFragmentManager(), nestedScrollWebView).execute(currentUri.getHost());
+ // Get the IP addresses for the host.
+ new GetHostIpAddresses(activity, getSupportFragmentManager(), nestedScrollWebView).execute(currentUri.getHost());
- // Replace Refresh with Stop if the options menu has been created. (The first WebView typically begins loading before the menu items are instantiated.)
- if (optionsMenu != null) {
- // Get a handle for the refresh menu item.
- MenuItem refreshMenuItem = optionsMenu.findItem(R.id.refresh);
+ // Replace Refresh with Stop if the options menu has been created. (The first WebView typically begins loading before the menu items are instantiated.)
+ if (optionsMenu != null) {
+ // Get a handle for the refresh menu item.
+ MenuItem refreshMenuItem = optionsMenu.findItem(R.id.refresh);
- // Set the title.
- refreshMenuItem.setTitle(R.string.stop);
+ // Set the title.
+ refreshMenuItem.setTitle(R.string.stop);
- // Get the app bar and theme preferences.
- boolean displayAdditionalAppBarIcons = sharedPreferences.getBoolean("display_additional_app_bar_icons", false);
+ // Get the app bar and theme preferences.
+ boolean displayAdditionalAppBarIcons = sharedPreferences.getBoolean("display_additional_app_bar_icons", false);
- // If the icon is displayed in the AppBar, set it according to the theme.
- if (displayAdditionalAppBarIcons) {
- if (darkTheme) {
- refreshMenuItem.setIcon(R.drawable.close_dark);
- } else {
- refreshMenuItem.setIcon(R.drawable.close_light);
- }
+ // If the icon is displayed in the AppBar, set it according to the theme.
+ if (displayAdditionalAppBarIcons) {
+ if (darkTheme) {
+ refreshMenuItem.setIcon(R.drawable.close_dark);
+ } else {
+ refreshMenuItem.setIcon(R.drawable.close_light);
}
}
}
}
}
- // Update the URL text box and apply domain settings if not waiting on Orbot.
- if (!waitingForOrbot) {
- // Get the current page position.
- int currentPagePosition = webViewPagerAdapter.getPositionForId(nestedScrollWebView.getWebViewFragmentId());
+ // Get the current page position.
+ int currentPagePosition = webViewPagerAdapter.getPositionForId(nestedScrollWebView.getWebViewFragmentId());
- // Check the current website information against any pinned domain information if the current IP addresses have been loaded.
- if ((nestedScrollWebView.hasPinnedSslCertificate() || nestedScrollWebView.hasPinnedIpAddresses()) && nestedScrollWebView.hasCurrentIpAddresses() &&
- !nestedScrollWebView.ignorePinnedDomainInformation()) {
- CheckPinnedMismatchHelper.checkPinnedMismatch(getSupportFragmentManager(), nestedScrollWebView);
- }
+ // Check the current website information against any pinned domain information if the current IP addresses have been loaded.
+ if ((nestedScrollWebView.hasPinnedSslCertificate() || nestedScrollWebView.hasPinnedIpAddresses()) && nestedScrollWebView.hasCurrentIpAddresses() &&
+ !nestedScrollWebView.ignorePinnedDomainInformation()) {
+ CheckPinnedMismatchHelper.checkPinnedMismatch(getSupportFragmentManager(), nestedScrollWebView);
+ }
- // Get the current URL from the nested scroll WebView. This is more accurate than using the URL passed into the method, which is sometimes not the final one.
- String currentUrl = nestedScrollWebView.getUrl();
+ // Get the current URL from the nested scroll WebView. This is more accurate than using the URL passed into the method, which is sometimes not the final one.
+ String currentUrl = nestedScrollWebView.getUrl();
- // Get the current tab.
- TabLayout.Tab tab = tabLayout.getTabAt(currentPagePosition);
+ // Get the current tab.
+ TabLayout.Tab tab = tabLayout.getTabAt(currentPagePosition);
- // Update the URL text bar if the page is currently selected and the user is not currently typing in the URL edit text.
- // Crash records show that, in some crazy way, it is possible for the current URL to be blank at this point.
- // Probably some sort of race condition when Privacy Browser is being resumed.
- if ((tabLayout.getSelectedTabPosition() == currentPagePosition) && !urlEditText.hasFocus() && (currentUrl != null)) {
- // Check to see if the URL is `about:blank`.
- if (currentUrl.equals("about:blank")) { // The WebView is blank.
- // Display the hint in the URL edit text.
- urlEditText.setText("");
+ // Update the URL text bar if the page is currently selected and the user is not currently typing in the URL edit text.
+ // Crash records show that, in some crazy way, it is possible for the current URL to be blank at this point.
+ // Probably some sort of race condition when Privacy Browser is being resumed.
+ if ((tabLayout.getSelectedTabPosition() == currentPagePosition) && !urlEditText.hasFocus() && (currentUrl != null)) {
+ // Check to see if the URL is `about:blank`.
+ if (currentUrl.equals("about:blank")) { // The WebView is blank.
+ // Display the hint in the URL edit text.
+ urlEditText.setText("");
- // Request focus for the URL text box.
- urlEditText.requestFocus();
+ // Request focus for the URL text box.
+ urlEditText.requestFocus();
- // Display the keyboard.
- inputMethodManager.showSoftInput(urlEditText, 0);
+ // Display the keyboard.
+ inputMethodManager.showSoftInput(urlEditText, 0);
- // Apply the domain settings. This clears any settings from the previous domain.
- applyDomainSettings(nestedScrollWebView, "", true, false);
+ // Apply the domain settings. This clears any settings from the previous domain.
+ applyDomainSettings(nestedScrollWebView, "", true, false);
- // Only populate the title text view if the tab has been fully created.
- if (tab != null) {
- // Get the custom view from the tab.
- View tabView = tab.getCustomView();
+ // Only populate the title text view if the tab has been fully created.
+ if (tab != null) {
+ // Get the custom view from the tab.
+ View tabView = tab.getCustomView();
- // Remove the incorrect warning below that the current tab view might be null.
- assert tabView != null;
+ // Remove the incorrect warning below that the current tab view might be null.
+ assert tabView != null;
- // Get the title text view from the tab.
- TextView tabTitleTextView = tabView.findViewById(R.id.title_textview);
+ // Get the title text view from the tab.
+ TextView tabTitleTextView = tabView.findViewById(R.id.title_textview);
- // Set the title as the tab text.
- tabTitleTextView.setText(R.string.new_tab);
- }
- } else { // The WebView has loaded a webpage.
- // Update the URL edit text if it is not currently being edited.
- if (!urlEditText.hasFocus()) {
- // Sanitize the current URL. This removes unwanted URL elements that were added by redirects, so that they won't be included if the URL is shared.
- String sanitizedUrl = sanitizeUrl(currentUrl);
+ // Set the title as the tab text.
+ tabTitleTextView.setText(R.string.new_tab);
+ }
+ } else { // The WebView has loaded a webpage.
+ // Update the URL edit text if it is not currently being edited.
+ if (!urlEditText.hasFocus()) {
+ // Sanitize the current URL. This removes unwanted URL elements that were added by redirects, so that they won't be included if the URL is shared.
+ String sanitizedUrl = sanitizeUrl(currentUrl);
- // Display the final URL. Getting the URL from the WebView instead of using the one provided by `onPageFinished()` makes websites like YouTube function correctly.
- urlEditText.setText(sanitizedUrl);
+ // Display the final URL. Getting the URL from the WebView instead of using the one provided by `onPageFinished()` makes websites like YouTube function correctly.
+ urlEditText.setText(sanitizedUrl);
- // Apply text highlighting to the URL.
- highlightUrlText();
- }
+ // Apply text highlighting to the URL.
+ highlightUrlText();
+ }
- // Only populate the title text view if the tab has been fully created.
- if (tab != null) {
- // Get the custom view from the tab.
- View tabView = tab.getCustomView();
+ // Only populate the title text view if the tab has been fully created.
+ if (tab != null) {
+ // Get the custom view from the tab.
+ View tabView = tab.getCustomView();
- // Remove the incorrect warning below that the current tab view might be null.
- assert tabView != null;
+ // Remove the incorrect warning below that the current tab view might be null.
+ assert tabView != null;
- // Get the title text view from the tab.
- TextView tabTitleTextView = tabView.findViewById(R.id.title_textview);
+ // Get the title text view from the tab.
+ TextView tabTitleTextView = tabView.findViewById(R.id.title_textview);
- // Set the title as the tab text. Sometimes `onReceivedTitle()` is not called, especially when navigating history.
- tabTitleTextView.setText(nestedScrollWebView.getTitle());
- }
+ // Set the title as the tab text. Sometimes `onReceivedTitle()` is not called, especially when navigating history.
+ tabTitleTextView.setText(nestedScrollWebView.getTitle());
}
}
}
// Apply the app settings from the shared preferences.
applyAppSettings();
- // Load the website if not waiting for Orbot to connect.
- if (!waitingForOrbot) {
- // Get the intent that started the app.
- Intent launchingIntent = getIntent();
+ // Initialize the URL to load string.
+ String urlToLoadString;
- // Get the information from the intent.
- String launchingIntentAction = launchingIntent.getAction();
- Uri launchingIntentUriData = launchingIntent.getData();
+ // Get the intent that started the app.
+ Intent launchingIntent = getIntent();
- // 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;
+ // Get the information from the intent.
+ String launchingIntentAction = launchingIntent.getAction();
+ Uri launchingIntentUriData = launchingIntent.getData();
+
+ // Parse the launching intent URL.
+ if ((launchingIntentAction != null) && launchingIntentAction.equals(Intent.ACTION_WEB_SEARCH)) { // The intent contains a search string.
+ // 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 = "";
+ }
- // Sanitize the search input and convert it to a search.
- try {
- encodedUrlString = URLEncoder.encode(launchingIntent.getStringExtra(SearchManager.QUERY), "UTF-8");
- } catch (UnsupportedEncodingException exception) {
- encodedUrlString = "";
- }
+ // Store the web search as the URL to load.
+ urlToLoadString = searchURL + encodedUrlString;
+ } else if (launchingIntentUriData != null){ // The intent contains a URL.
+ // Store the URL.
+ urlToLoadString = launchingIntentUriData.toString();
+ } else { // The is no URL in the intent.
+ // Store the homepage to be loaded.
+ urlToLoadString = sharedPreferences.getString("homepage", getString(R.string.homepage_default_value));
+ }
- // Load the completed search URL.
- loadUrl(searchURL + encodedUrlString);
- } else if (launchingIntentUriData != null){ // Check to see if the intent contains a new URL.
- // Load the URL from the intent.
- loadUrl(launchingIntentUriData.toString());
- } else { // The is no URL in the intent.
- // Select the homepage based on the proxy through Orbot status.
- if (proxyThroughOrbot) {
- // Load the Tor homepage.
- loadUrl(sharedPreferences.getString("tor_homepage", getString(R.string.tor_homepage_default_value)));
- } else {
- // Load the normal homepage.
- loadUrl(sharedPreferences.getString("homepage", getString(R.string.homepage_default_value)));
- }
- }
+ // Load the website if not waiting for the proxy.
+ if (waitingForProxy) { // Store the URL to be loaded in the Nested Scroll WebView.
+ nestedScrollWebView.setWaitingForProxyUrlString(urlToLoadString);
+ } else { // Load the URL.
+ loadUrl(nestedScrollWebView, urlToLoadString);
}
} else { // This is not the first tab.
// Apply the domain settings.