import android.content.BroadcastReceiver;
import android.content.ClipData;
import android.content.ClipboardManager;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.net.http.SslCertificate;
import android.net.http.SslError;
+import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
+import androidx.core.content.FileProvider;
+import androidx.core.content.res.ResourcesCompat;
import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.fragment.app.DialogFragment;
import com.stoutner.privacybrowser.dialogs.OpenDialog;
import com.stoutner.privacybrowser.dialogs.ProxyNotInstalledDialog;
import com.stoutner.privacybrowser.dialogs.PinnedMismatchDialog;
-import com.stoutner.privacybrowser.dialogs.SaveDialog;
+import com.stoutner.privacybrowser.dialogs.SaveWebpageDialog;
import com.stoutner.privacybrowser.dialogs.SslCertificateErrorDialog;
import com.stoutner.privacybrowser.dialogs.StoragePermissionDialog;
import com.stoutner.privacybrowser.dialogs.UrlHistoryDialog;
import com.stoutner.privacybrowser.helpers.AdHelper;
import com.stoutner.privacybrowser.helpers.BlocklistHelper;
import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper;
-import com.stoutner.privacybrowser.helpers.CheckPinnedMismatchHelper;
import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper;
import com.stoutner.privacybrowser.helpers.FileNameHelper;
import com.stoutner.privacybrowser.helpers.ProxyHelper;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
public class MainWebViewActivity extends AppCompatActivity implements CreateBookmarkDialog.CreateBookmarkListener, CreateBookmarkFolderDialog.CreateBookmarkFolderListener,
EditBookmarkFolderDialog.EditBookmarkFolderListener, FontSizeDialog.UpdateFontSizeListener, NavigationView.OnNavigationItemSelectedListener, OpenDialog.OpenListener,
- PinnedMismatchDialog.PinnedMismatchListener, PopulateBlocklists.PopulateBlocklistsListener, SaveDialog.SaveWebpageListener, StoragePermissionDialog.StoragePermissionDialogListener,
+ PinnedMismatchDialog.PinnedMismatchListener, PopulateBlocklists.PopulateBlocklistsListener, SaveWebpageDialog.SaveWebpageListener, StoragePermissionDialog.StoragePermissionDialogListener,
UrlHistoryDialog.NavigateHistoryListener, WebViewTabFragment.NewTabListener {
+ // The executor service handles background tasks. It is accessed from `ViewSourceActivity`.
+ public static ExecutorService executorService = Executors.newFixedThreadPool(4);
+
// `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;
- // The load URL on restart variables are public static so they can be accessed from `BookmarksActivity`. They are used in `onRestart()`.
- public static boolean loadUrlOnRestart;
- public static String urlToLoadOnRestart;
-
// `restartFromBookmarksActivity` is public static so it can be accessed from `BookmarksActivity`. It is also used in `onRestart()`.
public static boolean restartFromBookmarksActivity;
// 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;
+ // Define the saved instance state constants.
+ private final String SAVED_STATE_ARRAY_LIST = "saved_state_array_list";
+ private final String SAVED_NESTED_SCROLL_WEBVIEW_STATE_ARRAY_LIST = "saved_nested_scroll_webview_state_array_list";
+ private final String SAVED_TAB_POSITION = "saved_tab_position";
+ private final String PROXY_MODE = "proxy_mode";
- // 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;
+ // Define the saved instance state variables.
+ private ArrayList<Bundle> savedStateArrayList;
+ private ArrayList<Bundle> savedNestedScrollWebViewStateArrayList;
+ private int savedTabPosition;
+ private String savedProxyMode;
+
+ // Define the class variables.
+ @SuppressWarnings("rawtypes")
+ AsyncTask populateBlocklists;
// The current WebView is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onRestart()`, `onCreateContextMenu()`, `findPreviousOnPage()`,
// `findNextOnPage()`, `closeFindOnPage()`, `loadUrlFromTextBox()`, `onSslMismatchBack()`, `applyProxy()`, and `applyDomainSettings()`.
private String saveWebpageUrl;
private String saveWebpageFilePath;
+ // Declare the class views.
+ private DrawerLayout drawerLayout;
+ private AppBarLayout appBarLayout;
+ private Toolbar toolbar;
+ private LinearLayout findOnPageLinearLayout;
+ private LinearLayout tabsLinearLayout;
+ private TabLayout tabLayout;
+ private SwipeRefreshLayout swipeRefreshLayout;
+ private ViewPager webViewPager;
+
@Override
// Remove the warning about needing to override `performClick()` when using an `OnTouchListener` with `WebView`.
@SuppressLint("ClickableViewAccessibility")
// Run the default commands.
super.onCreate(savedInstanceState);
+ // Check to see if the activity has been restarted.
+ if (savedInstanceState != null) {
+ // Store the saved instance state variables.
+ savedStateArrayList = savedInstanceState.getParcelableArrayList(SAVED_STATE_ARRAY_LIST);
+ savedNestedScrollWebViewStateArrayList = savedInstanceState.getParcelableArrayList(SAVED_NESTED_SCROLL_WEBVIEW_STATE_ARRAY_LIST);
+ savedTabPosition = savedInstanceState.getInt(SAVED_TAB_POSITION);
+ savedProxyMode = savedInstanceState.getString(PROXY_MODE);
+ }
+
// Initialize the default preference values the first time the program is run. `false` keeps this command from resetting any current preferences back to default.
PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
// Get the screenshot preference.
String appTheme = sharedPreferences.getString("app_theme", getString(R.string.app_theme_default_value));
- boolean allowScreenshots = sharedPreferences.getBoolean("allow_screenshots", false);
+ boolean allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false);
// Get the theme entry values string array.
String[] appThemeEntryValuesStringArray = getResources().getStringArray(R.array.app_theme_entry_values);
// Set the content view.
setContentView(R.layout.main_framelayout);
- // Get handles for the views that need to be modified.
- DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
- Toolbar toolbar = findViewById(R.id.toolbar);
- ViewPager webViewPager = findViewById(R.id.webviewpager);
+ // Get handles for the views.
+ drawerLayout = findViewById(R.id.drawerlayout);
+ appBarLayout = findViewById(R.id.appbar_layout);
+ toolbar = findViewById(R.id.toolbar);
+ findOnPageLinearLayout = findViewById(R.id.find_on_page_linearlayout);
+ tabsLinearLayout = findViewById(R.id.tabs_linearlayout);
+ tabLayout = findViewById(R.id.tablayout);
+ swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
+ webViewPager = findViewById(R.id.webviewpager);
// Get a handle for the app compat delegate.
AppCompatDelegate appCompatDelegate = getDelegate();
// Store up to 100 tabs in memory.
webViewPager.setOffscreenPageLimit(100);
+ // Initialize the app.
+ initializeApp();
+
+ // Apply the app settings from the shared preferences.
+ applyAppSettings();
+
// Populate the blocklists.
- new PopulateBlocklists(this, this).execute();
+ populateBlocklists = new PopulateBlocklists(this, this).execute();
}
@Override
// Replace the intent that started the app with this one.
setIntent(intent);
- // Process the intent here if Privacy Browser is fully initialized. If the process has been killed by the system while sitting in the background, this will be handled in `initializeWebView()`.
- if (ultraPrivacy != null) {
+ // Check to see if the app is being restarted from a saved state.
+ if (savedStateArrayList == null || savedStateArrayList.size() == 0) { // The activity is not being restarted from a saved state.
// Get the information from the intent.
String intentAction = intent.getAction();
Uri intentUriData = intent.getData();
+ String intentStringExtra = intent.getStringExtra(Intent.EXTRA_TEXT);
// Determine if this is a web search.
boolean isWebSearch = ((intentAction != null) && intentAction.equals(Intent.ACTION_WEB_SEARCH));
// Only process the URI if it contains data or it is a web search. If the user pressed the desktop icon after the app was already running the URI will be null.
- if (intentUriData != null || isWebSearch) {
+ if (intentUriData != null || intentStringExtra != null || isWebSearch) {
// Get the shared preferences.
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
String url;
// If the intent action is a web search, perform the search.
- if (isWebSearch) {
+ if (isWebSearch) { // The intent is a web search.
// Create an encoded URL string.
String encodedUrlString;
// Add the base search URL.
url = searchURL + encodedUrlString;
- } else { // The intent should contain a URL.
+ } else if (intentUriData != null) { // The intent contains a URL formatted as a URI.
// Set the intent data as the URL.
url = intentUriData.toString();
+ } else { // The intent contains a string, which might be a URL.
+ // Set the intent string as the URL.
+ url = intentStringExtra;
}
// Add a new tab if specified in the preferences.
loadUrl(currentWebView, url);
}
- // Get a handle for the drawer layout.
- DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
-
// Close the navigation drawer if it is open.
if (drawerLayout.isDrawerVisible(GravityCompat.START)) {
drawerLayout.closeDrawer(GravityCompat.START);
// Reapply the domain settings if the URL is not null, which can happen if an empty tab is active when returning from settings.
if (nestedScrollWebView.getUrl() != null) {
- applyDomainSettings(nestedScrollWebView, nestedScrollWebView.getUrl(), false, true);
+ applyDomainSettings(nestedScrollWebView, nestedScrollWebView.getUrl(), false, true, false);
}
}
}
}
- // Load the URL on restart (used when loading a bookmark).
- if (loadUrlOnRestart) {
- // Load the specified URL.
- loadUrl(currentWebView, urlToLoadOnRestart);
-
- // Reset the load on restart tracker.
- loadUrlOnRestart = false;
- }
-
// Update the bookmarks drawer if returning from the Bookmarks activity.
if (restartFromBookmarksActivity) {
- // Get a handle for the drawer layout.
- DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
-
// Close the bookmarks drawer.
drawerLayout.closeDrawer(GravityCompat.END);
// Get the nested scroll WebView from the tab fragment.
NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
- // Resume the nested scroll WebView JavaScript timers.
- nestedScrollWebView.resumeTimers();
-
// Resume the nested scroll WebView.
nestedScrollWebView.onResume();
}
}
+ // Resume the nested scroll WebView JavaScript timers. This is a global command that resumes JavaScript timers on all WebViews.
+ if (currentWebView != null) {
+ currentWebView.resumeTimers();
+ }
+
// Reapply the proxy settings if the system is using a proxy. This redisplays the appropriate alert dialog.
if (!proxyMode.equals(ProxyHelper.NONE)) {
applyProxy(false);
// Pause the nested scroll WebView.
nestedScrollWebView.onPause();
-
- // Pause the nested scroll WebView JavaScript timers.
- nestedScrollWebView.pauseTimers();
}
}
+ // Pause the WebView JavaScript timers. This is a global command that pauses JavaScript on all WebViews.
+ if (currentWebView != null) {
+ currentWebView.pauseTimers();
+ }
+
// Pause the ad or it will continue to consume resources in the background on the free flavor.
if (BuildConfig.FLAVOR.contentEquals("free")) {
// Pause the ad.
}
}
+ @Override
+ public void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
+ // Run the default commands.
+ super.onSaveInstanceState(savedInstanceState);
+
+ // Create the saved state array lists.
+ ArrayList<Bundle> savedStateArrayList = new ArrayList<>();
+ ArrayList<Bundle> savedNestedScrollWebViewStateArrayList = new ArrayList<>();
+
+ // Get the URLs from each tab.
+ 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();
+
+ if (fragmentView != null) {
+ // Get the nested scroll WebView from the tab fragment.
+ NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
+
+ // Create saved state bundle.
+ Bundle savedStateBundle = new Bundle();
+
+ // Get the current states.
+ nestedScrollWebView.saveState(savedStateBundle);
+ Bundle savedNestedScrollWebViewStateBundle = nestedScrollWebView.saveNestedScrollWebViewState();
+
+ // Store the saved states in the array lists.
+ savedStateArrayList.add(savedStateBundle);
+ savedNestedScrollWebViewStateArrayList.add(savedNestedScrollWebViewStateBundle);
+ }
+ }
+
+ // Get the current tab position.
+ int currentTabPosition = tabLayout.getSelectedTabPosition();
+
+ // Store the saved states in the bundle.
+ savedInstanceState.putParcelableArrayList(SAVED_STATE_ARRAY_LIST, savedStateArrayList);
+ savedInstanceState.putParcelableArrayList(SAVED_NESTED_SCROLL_WEBVIEW_STATE_ARRAY_LIST, savedNestedScrollWebViewStateArrayList);
+ savedInstanceState.putInt(SAVED_TAB_POSITION, currentTabPosition);
+ savedInstanceState.putString(PROXY_MODE, proxyMode);
+ }
+
@Override
public void onDestroy() {
// Unregister the orbot status broadcast receiver if it exists.
bookmarksDatabaseHelper.close();
}
+ // Stop populating the blocklists if the AsyncTask is running in the background.
+ if (populateBlocklists != null) {
+ populateBlocklists.cancel(true);
+ }
+
// Run the default commands.
super.onDestroy();
}
updatePrivacyIcons(false);
// Get handles for the menu items.
+ MenuItem bookmarksMenuItem = menu.findItem(R.id.bookmarks);
MenuItem toggleFirstPartyCookiesMenuItem = menu.findItem(R.id.toggle_first_party_cookies);
MenuItem toggleThirdPartyCookiesMenuItem = menu.findItem(R.id.toggle_third_party_cookies);
- MenuItem toggleDomStorageMenuItem = menu.findItem(R.id.toggle_dom_storage);
MenuItem toggleSaveFormDataMenuItem = menu.findItem(R.id.toggle_save_form_data); // Form data can be removed once the minimum API >= 26.
MenuItem clearFormDataMenuItem = menu.findItem(R.id.clear_form_data); // Form data can be removed once the minimum API >= 26.
MenuItem refreshMenuItem = menu.findItem(R.id.refresh);
+ MenuItem darkWebViewMenuItem = menu.findItem(R.id.dark_webview);
MenuItem adConsentMenuItem = menu.findItem(R.id.ad_consent);
// Only display third-party cookies if API >= 21
// Disable the clear form data menu item if the API >= 26 so that the status of the main Clear Data is calculated correctly.
clearFormDataMenuItem.setEnabled(Build.VERSION.SDK_INT < 26);
+ // Only display the dark WebView menu item if API >= 21.
+ darkWebViewMenuItem.setVisible(Build.VERSION.SDK_INT >= 21);
+
// Only show Ad Consent if this is the free flavor.
adConsentMenuItem.setVisible(BuildConfig.FLAVOR.contentEquals("free"));
// Get the shared preferences.
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
- // Get the dark theme and app bar preferences..
- boolean displayAdditionalAppBarIcons = sharedPreferences.getBoolean("display_additional_app_bar_icons", false);
+ // Get the dark theme and app bar preferences.
+ boolean displayAdditionalAppBarIcons = sharedPreferences.getBoolean(getString(R.string.display_additional_app_bar_icons_key), false);
// Set the status of the additional app bar icons. Setting the refresh menu item to `SHOW_AS_ACTION_ALWAYS` makes it appear even on small devices like phones.
if (displayAdditionalAppBarIcons) {
- toggleFirstPartyCookiesMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
- toggleDomStorageMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
refreshMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+ bookmarksMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ toggleFirstPartyCookiesMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
} else { //Do not display the additional icons.
- toggleFirstPartyCookiesMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
- toggleDomStorageMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
refreshMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+ bookmarksMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+ toggleFirstPartyCookiesMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
}
// Replace Refresh with Stop if a URL is already loading.
int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
// Set the icon according to the current theme status.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- // Set the dark stop icon.
- refreshMenuItem.setIcon(R.drawable.close_night);
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+ refreshMenuItem.setIcon(R.drawable.close_blue_day);
} else {
- // Set the light stop icon.
- refreshMenuItem.setIcon(R.drawable.close_day);
+ refreshMenuItem.setIcon(R.drawable.close_blue_night);
}
}
}
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 darkWebViewMenuItem = menu.findItem(R.id.dark_webview);
// Get a handle for the cookie manager.
CookieManager cookieManager = CookieManager.getInstance();
swipeToRefreshMenuItem.setChecked(currentWebView.getSwipeToRefresh());
wideViewportMenuItem.setChecked(currentWebView.getSettings().getUseWideViewPort());
displayImagesMenuItem.setChecked(currentWebView.getSettings().getLoadsImagesAutomatically());
- nightModeMenuItem.setChecked(currentWebView.getNightMode());
// Initialize the display names for the blocklists with the number of blocked requests.
blocklistsMenuItem.setTitle(getString(R.string.blocklists) + " - " + currentWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
// Enable DOM Storage if JavaScript is enabled.
domStorageMenuItem.setEnabled(currentWebView.getSettings().getJavaScriptEnabled());
+
+ // Set the checkbox status for dark WebView if the WebView supports it.
+ if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
+ darkWebViewMenuItem.setChecked(WebSettingsCompat.getForceDark(currentWebView.getSettings()) == WebSettingsCompat.FORCE_DARK_ON);
+ }
}
// Set the checked status of the first party cookies menu item.
}
@Override
- // Remove Android Studio's warning about the dangers of using SetJavaScriptEnabled.
- @SuppressLint("SetJavaScriptEnabled")
public boolean onOptionsItemSelected(MenuItem menuItem) {
- // Get the selected menu item ID.
- int menuItemId = menuItem.getItemId();
-
// Get a handle for the shared preferences.
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
// Get a handle for the cookie manager.
CookieManager cookieManager = CookieManager.getInstance();
+ // Get the selected menu item ID.
+ int menuItemId = menuItem.getItemId();
+
// Run the commands that correlate to the selected menu item.
- switch (menuItemId) {
- case R.id.toggle_javascript:
- // Toggle the JavaScript status.
- currentWebView.getSettings().setJavaScriptEnabled(!currentWebView.getSettings().getJavaScriptEnabled());
-
- // Update the privacy icon. `true` runs `invalidateOptionsMenu` as the last step.
- updatePrivacyIcons(true);
-
- // Display a `Snackbar`.
- if (currentWebView.getSettings().getJavaScriptEnabled()) { // JavaScrip is enabled.
- Snackbar.make(findViewById(R.id.webviewpager), R.string.javascript_enabled, Snackbar.LENGTH_SHORT).show();
- } else if (cookieManager.acceptCookie()) { // JavaScript is disabled, but first-party cookies are enabled.
- Snackbar.make(findViewById(R.id.webviewpager), R.string.javascript_disabled, Snackbar.LENGTH_SHORT).show();
- } else { // Privacy mode.
- Snackbar.make(findViewById(R.id.webviewpager), R.string.privacy_mode, Snackbar.LENGTH_SHORT).show();
- }
+ if (menuItemId == R.id.toggle_javascript) { // JavaScript.
+ // Toggle the JavaScript status.
+ currentWebView.getSettings().setJavaScriptEnabled(!currentWebView.getSettings().getJavaScriptEnabled());
+ // Update the privacy icon.
+ updatePrivacyIcons(true);
+
+ // Display a `Snackbar`.
+ if (currentWebView.getSettings().getJavaScriptEnabled()) { // JavaScrip is enabled.
+ Snackbar.make(webViewPager, R.string.javascript_enabled, Snackbar.LENGTH_SHORT).show();
+ } else if (cookieManager.acceptCookie()) { // JavaScript is disabled, but first-party cookies are enabled.
+ Snackbar.make(webViewPager, R.string.javascript_disabled, Snackbar.LENGTH_SHORT).show();
+ } else { // Privacy mode.
+ Snackbar.make(webViewPager, R.string.privacy_mode, Snackbar.LENGTH_SHORT).show();
+ }
+
+ // Reload the current WebView.
+ currentWebView.reload();
+
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.refresh) { // Refresh.
+ // Run the command that correlates to the current status of the menu item.
+ 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();
+ }
- // Consume the event.
- return true;
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.bookmarks) { // Bookmarks.
+ // Open the bookmarks drawer.
+ drawerLayout.openDrawer(GravityCompat.END);
- 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();
- }
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.toggle_first_party_cookies) { // First-party cookies.
+ // Switch the first-party cookie status.
+ cookieManager.setAcceptCookie(!cookieManager.acceptCookie());
- // Consume the event.
- return true;
+ // Store the first-party cookie status.
+ currentWebView.setAcceptFirstPartyCookies(cookieManager.acceptCookie());
- case R.id.bookmarks:
- // Get a handle for the drawer layout.
- DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
+ // Update the menu checkbox.
+ menuItem.setChecked(cookieManager.acceptCookie());
- // Open the bookmarks drawer.
- drawerLayout.openDrawer(GravityCompat.END);
+ // Update the privacy icon.
+ updatePrivacyIcons(true);
- // Consume the event.
- return true;
+ // Display a snackbar.
+ if (cookieManager.acceptCookie()) { // First-party cookies are enabled.
+ Snackbar.make(webViewPager, R.string.first_party_cookies_enabled, Snackbar.LENGTH_SHORT).show();
+ } else if (currentWebView.getSettings().getJavaScriptEnabled()) { // JavaScript is still enabled.
+ Snackbar.make(webViewPager, R.string.first_party_cookies_disabled, Snackbar.LENGTH_SHORT).show();
+ } else { // Privacy mode.
+ Snackbar.make(webViewPager, R.string.privacy_mode, Snackbar.LENGTH_SHORT).show();
+ }
- case R.id.toggle_first_party_cookies:
- // Switch the first-party cookie status.
- cookieManager.setAcceptCookie(!cookieManager.acceptCookie());
+ // Reload the current WebView.
+ currentWebView.reload();
- // Store the first-party cookie status.
- currentWebView.setAcceptFirstPartyCookies(cookieManager.acceptCookie());
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.toggle_third_party_cookies) { // Third-party cookies.
+ // Only act if the API >= 21. Otherwise, there are no third-party cookie controls.
+ if (Build.VERSION.SDK_INT >= 21) {
+ // Toggle the status of thirdPartyCookiesEnabled.
+ cookieManager.setAcceptThirdPartyCookies(currentWebView, !cookieManager.acceptThirdPartyCookies(currentWebView));
// Update the menu checkbox.
- menuItem.setChecked(cookieManager.acceptCookie());
-
- // Update the privacy icon. `true` runs `invalidateOptionsMenu` as the last step.
- updatePrivacyIcons(true);
+ menuItem.setChecked(cookieManager.acceptThirdPartyCookies(currentWebView));
// Display a snackbar.
- if (cookieManager.acceptCookie()) { // First-party cookies are enabled.
- Snackbar.make(findViewById(R.id.webviewpager), R.string.first_party_cookies_enabled, Snackbar.LENGTH_SHORT).show();
- } else if (currentWebView.getSettings().getJavaScriptEnabled()) { // JavaScript is still enabled.
- Snackbar.make(findViewById(R.id.webviewpager), R.string.first_party_cookies_disabled, Snackbar.LENGTH_SHORT).show();
- } else { // Privacy mode.
- Snackbar.make(findViewById(R.id.webviewpager), R.string.privacy_mode, Snackbar.LENGTH_SHORT).show();
+ if (cookieManager.acceptThirdPartyCookies(currentWebView)) {
+ Snackbar.make(webViewPager, R.string.third_party_cookies_enabled, Snackbar.LENGTH_SHORT).show();
+ } else {
+ Snackbar.make(webViewPager, R.string.third_party_cookies_disabled, Snackbar.LENGTH_SHORT).show();
}
// Reload the current WebView.
currentWebView.reload();
+ }
- // Consume the event.
- return true;
-
- case R.id.toggle_third_party_cookies:
- if (Build.VERSION.SDK_INT >= 21) {
- // Switch the status of thirdPartyCookiesEnabled.
- cookieManager.setAcceptThirdPartyCookies(currentWebView, !cookieManager.acceptThirdPartyCookies(currentWebView));
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.toggle_dom_storage) { // DOM storage.
+ // Toggle the status of domStorageEnabled.
+ currentWebView.getSettings().setDomStorageEnabled(!currentWebView.getSettings().getDomStorageEnabled());
- // Update the menu checkbox.
- menuItem.setChecked(cookieManager.acceptThirdPartyCookies(currentWebView));
+ // Update the menu checkbox.
+ menuItem.setChecked(currentWebView.getSettings().getDomStorageEnabled());
- // Display a snackbar.
- if (cookieManager.acceptThirdPartyCookies(currentWebView)) {
- Snackbar.make(findViewById(R.id.webviewpager), R.string.third_party_cookies_enabled, Snackbar.LENGTH_SHORT).show();
- } else {
- Snackbar.make(findViewById(R.id.webviewpager), R.string.third_party_cookies_disabled, Snackbar.LENGTH_SHORT).show();
- }
+ // Update the privacy icon.
+ updatePrivacyIcons(true);
- // Reload the current WebView.
- currentWebView.reload();
- } // Else do nothing because SDK < 21.
+ // Display a snackbar.
+ if (currentWebView.getSettings().getDomStorageEnabled()) {
+ Snackbar.make(webViewPager, R.string.dom_storage_enabled, Snackbar.LENGTH_SHORT).show();
+ } else {
+ Snackbar.make(webViewPager, R.string.dom_storage_disabled, Snackbar.LENGTH_SHORT).show();
+ }
- // Consume the event.
- return true;
+ // Reload the current WebView.
+ currentWebView.reload();
- case R.id.toggle_dom_storage:
- // Toggle the status of domStorageEnabled.
- currentWebView.getSettings().setDomStorageEnabled(!currentWebView.getSettings().getDomStorageEnabled());
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.toggle_save_form_data) { // Form data. This can be removed once the minimum API >= 26.
+ // Switch the status of saveFormDataEnabled.
+ currentWebView.getSettings().setSaveFormData(!currentWebView.getSettings().getSaveFormData());
- // Update the menu checkbox.
- menuItem.setChecked(currentWebView.getSettings().getDomStorageEnabled());
+ // Update the menu checkbox.
+ menuItem.setChecked(currentWebView.getSettings().getSaveFormData());
- // Update the privacy icon. `true` refreshes the app bar icons.
- updatePrivacyIcons(true);
+ // Display a snackbar.
+ if (currentWebView.getSettings().getSaveFormData()) {
+ Snackbar.make(webViewPager, R.string.form_data_enabled, Snackbar.LENGTH_SHORT).show();
+ } else {
+ Snackbar.make(webViewPager, R.string.form_data_disabled, Snackbar.LENGTH_SHORT).show();
+ }
- // Display a snackbar.
- if (currentWebView.getSettings().getDomStorageEnabled()) {
- Snackbar.make(findViewById(R.id.webviewpager), R.string.dom_storage_enabled, Snackbar.LENGTH_SHORT).show();
- } else {
- Snackbar.make(findViewById(R.id.webviewpager), R.string.dom_storage_disabled, Snackbar.LENGTH_SHORT).show();
- }
+ // Update the privacy icon.
+ updatePrivacyIcons(true);
- // Reload the current WebView.
- currentWebView.reload();
+ // Reload the current WebView.
+ currentWebView.reload();
- // Consume the event.
- return true;
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.clear_cookies) { // Clear cookies.
+ // Create a snackbar.
+ Snackbar.make(webViewPager, R.string.cookies_deleted, Snackbar.LENGTH_LONG)
+ .setAction(R.string.undo, v -> {
+ // Do nothing because everything will be handled by `onDismissed()` below.
+ })
+ .addCallback(new Snackbar.Callback() {
+ @Override
+ public void onDismissed(Snackbar snackbar, int event) {
+ if (event != Snackbar.Callback.DISMISS_EVENT_ACTION) { // The snackbar was dismissed without the undo button being pushed.
+ // Delete the cookies, which command varies by SDK.
+ if (Build.VERSION.SDK_INT < 21) {
+ cookieManager.removeAllCookie();
+ } else {
+ cookieManager.removeAllCookies(null);
+ }
+ }
+ }
+ })
+ .show();
- // Form data can be removed once the minimum API >= 26.
- case R.id.toggle_save_form_data:
- // Switch the status of saveFormDataEnabled.
- currentWebView.getSettings().setSaveFormData(!currentWebView.getSettings().getSaveFormData());
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.clear_dom_storage) { // Clear DOM storage.
+ // Create a snackbar.
+ Snackbar.make(webViewPager, R.string.dom_storage_deleted, Snackbar.LENGTH_LONG)
+ .setAction(R.string.undo, v -> {
+ // Do nothing because everything will be handled by `onDismissed()` below.
+ })
+ .addCallback(new Snackbar.Callback() {
+ @Override
+ public void onDismissed(Snackbar snackbar, int event) {
+ if (event != Snackbar.Callback.DISMISS_EVENT_ACTION) { // The snackbar was dismissed without the undo button being pushed.
+ // Delete the DOM Storage.
+ WebStorage webStorage = WebStorage.getInstance();
+ webStorage.deleteAllData();
+
+ // Initialize a handler to manually delete the DOM storage files and directories.
+ Handler deleteDomStorageHandler = new Handler();
+
+ // Setup a runnable to manually delete the DOM storage files and directories.
+ Runnable deleteDomStorageRunnable = () -> {
+ try {
+ // Get a handle for the runtime.
+ Runtime runtime = Runtime.getRuntime();
+
+ // Get the application's private data directory, which will be something like `/data/user/0/com.stoutner.privacybrowser.standard`,
+ // which links to `/data/data/com.stoutner.privacybrowser.standard`.
+ String privateDataDirectoryString = getApplicationInfo().dataDir;
+
+ // A string array must be used because the directory contains a space and `Runtime.exec` will otherwise not escape the string correctly.
+ Process deleteLocalStorageProcess = runtime.exec(new String[]{"rm", "-rf", privateDataDirectoryString + "/app_webview/Local Storage/"});
+
+ // Multiple commands must be used because `Runtime.exec()` does not like `*`.
+ Process deleteIndexProcess = runtime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/IndexedDB");
+ Process deleteQuotaManagerProcess = runtime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager");
+ Process deleteQuotaManagerJournalProcess = runtime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager-journal");
+ Process deleteDatabasesProcess = runtime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/databases");
+
+ // Wait for the processes to finish.
+ deleteLocalStorageProcess.waitFor();
+ deleteIndexProcess.waitFor();
+ deleteQuotaManagerProcess.waitFor();
+ deleteQuotaManagerJournalProcess.waitFor();
+ deleteDatabasesProcess.waitFor();
+ } catch (Exception exception) {
+ // Do nothing if an error is thrown.
+ }
+ };
- // Update the menu checkbox.
- menuItem.setChecked(currentWebView.getSettings().getSaveFormData());
+ // Manually delete the DOM storage files after 200 milliseconds.
+ deleteDomStorageHandler.postDelayed(deleteDomStorageRunnable, 200);
+ }
+ }
+ })
+ .show();
- // Display a snackbar.
- if (currentWebView.getSettings().getSaveFormData()) {
- Snackbar.make(findViewById(R.id.webviewpager), R.string.form_data_enabled, Snackbar.LENGTH_SHORT).show();
- } else {
- Snackbar.make(findViewById(R.id.webviewpager), R.string.form_data_disabled, Snackbar.LENGTH_SHORT).show();
- }
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.clear_form_data) { // Clear form data. This can be remove once the minimum API >= 26.
+ // Create a snackbar.
+ Snackbar.make(webViewPager, R.string.form_data_deleted, Snackbar.LENGTH_LONG)
+ .setAction(R.string.undo, v -> {
+ // Do nothing because everything will be handled by `onDismissed()` below.
+ })
+ .addCallback(new Snackbar.Callback() {
+ @Override
+ public void onDismissed(Snackbar snackbar, int event) {
+ if (event != Snackbar.Callback.DISMISS_EVENT_ACTION) { // The snackbar was dismissed without the undo button being pushed.
+ // Get a handle for the webView database.
+ WebViewDatabase webViewDatabase = WebViewDatabase.getInstance(getApplicationContext());
+
+ // Delete the form data.
+ webViewDatabase.clearFormData();
+ }
+ }
+ })
+ .show();
- // Update the privacy icon. `true` runs `invalidateOptionsMenu` as the last step.
- updatePrivacyIcons(true);
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.easylist) { // EasyList.
+ // Toggle the EasyList status.
+ currentWebView.enableBlocklist(NestedScrollWebView.EASYLIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYLIST));
- // Reload the current WebView.
- currentWebView.reload();
+ // Update the menu checkbox.
+ menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYLIST));
- // Consume the event.
- return true;
+ // Reload the current WebView.
+ currentWebView.reload();
- case R.id.clear_cookies:
- Snackbar.make(findViewById(R.id.webviewpager), R.string.cookies_deleted, Snackbar.LENGTH_LONG)
- .setAction(R.string.undo, v -> {
- // Do nothing because everything will be handled by `onDismissed()` below.
- })
- .addCallback(new Snackbar.Callback() {
- @SuppressLint("SwitchIntDef") // Ignore the lint warning about not handling the other possible events as they are covered by `default:`.
- @Override
- public void onDismissed(Snackbar snackbar, int event) {
- if (event != Snackbar.Callback.DISMISS_EVENT_ACTION) { // The snackbar was dismissed without the undo button being pushed.
- // Delete the cookies, which command varies by SDK.
- if (Build.VERSION.SDK_INT < 21) {
- cookieManager.removeAllCookie();
- } else {
- cookieManager.removeAllCookies(null);
- }
- }
- }
- })
- .show();
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.easyprivacy) { // EasyPrivacy.
+ // Toggle the EasyPrivacy status.
+ currentWebView.enableBlocklist(NestedScrollWebView.EASYPRIVACY, !currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYPRIVACY));
- // Consume the event.
- return true;
+ // Update the menu checkbox.
+ menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYPRIVACY));
- case R.id.clear_dom_storage:
- Snackbar.make(findViewById(R.id.webviewpager), R.string.dom_storage_deleted, Snackbar.LENGTH_LONG)
- .setAction(R.string.undo, v -> {
- // Do nothing because everything will be handled by `onDismissed()` below.
- })
- .addCallback(new Snackbar.Callback() {
- @SuppressLint("SwitchIntDef") // Ignore the lint warning about not handling the other possible events as they are covered by `default:`.
- @Override
- public void onDismissed(Snackbar snackbar, int event) {
- if (event != Snackbar.Callback.DISMISS_EVENT_ACTION) { // The snackbar was dismissed without the undo button being pushed.
- // Delete the DOM Storage.
- WebStorage webStorage = WebStorage.getInstance();
- webStorage.deleteAllData();
-
- // Initialize a handler to manually delete the DOM storage files and directories.
- Handler deleteDomStorageHandler = new Handler();
-
- // Setup a runnable to manually delete the DOM storage files and directories.
- Runnable deleteDomStorageRunnable = () -> {
- try {
- // Get a handle for the runtime.
- Runtime runtime = Runtime.getRuntime();
-
- // Get the application's private data directory, which will be something like `/data/user/0/com.stoutner.privacybrowser.standard`,
- // which links to `/data/data/com.stoutner.privacybrowser.standard`.
- String privateDataDirectoryString = getApplicationInfo().dataDir;
-
- // A string array must be used because the directory contains a space and `Runtime.exec` will otherwise not escape the string correctly.
- Process deleteLocalStorageProcess = runtime.exec(new String[]{"rm", "-rf", privateDataDirectoryString + "/app_webview/Local Storage/"});
-
- // Multiple commands must be used because `Runtime.exec()` does not like `*`.
- Process deleteIndexProcess = runtime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/IndexedDB");
- Process deleteQuotaManagerProcess = runtime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager");
- Process deleteQuotaManagerJournalProcess = runtime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager-journal");
- Process deleteDatabasesProcess = runtime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/databases");
-
- // Wait for the processes to finish.
- deleteLocalStorageProcess.waitFor();
- deleteIndexProcess.waitFor();
- deleteQuotaManagerProcess.waitFor();
- deleteQuotaManagerJournalProcess.waitFor();
- deleteDatabasesProcess.waitFor();
- } catch (Exception exception) {
- // Do nothing if an error is thrown.
- }
- };
-
- // Manually delete the DOM storage files after 200 milliseconds.
- deleteDomStorageHandler.postDelayed(deleteDomStorageRunnable, 200);
- }
- }
- })
- .show();
+ // Reload the current WebView.
+ currentWebView.reload();
- // Consume the event.
- return true;
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.fanboys_annoyance_list) { // Fanboy's Annoyance List.
+ // Toggle Fanboy's Annoyance List status.
+ currentWebView.enableBlocklist(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST));
- // Form data can be remove once the minimum API >= 26.
- case R.id.clear_form_data:
- Snackbar.make(findViewById(R.id.webviewpager), R.string.form_data_deleted, Snackbar.LENGTH_LONG)
- .setAction(R.string.undo, v -> {
- // Do nothing because everything will be handled by `onDismissed()` below.
- })
- .addCallback(new Snackbar.Callback() {
- @SuppressLint("SwitchIntDef") // Ignore the lint warning about not handling the other possible events as they are covered by `default:`.
- @Override
- public void onDismissed(Snackbar snackbar, int event) {
- if (event != Snackbar.Callback.DISMISS_EVENT_ACTION) { // The snackbar was dismissed without the undo button being pushed.
- // Delete the form data.
- WebViewDatabase mainWebViewDatabase = WebViewDatabase.getInstance(getApplicationContext());
- mainWebViewDatabase.clearFormData();
- }
- }
- })
- .show();
+ // Update the menu checkbox.
+ menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST));
- // Consume the event.
- return true;
+ // Get a handle for the Fanboy's Social Block List menu item.
+ MenuItem fanboysSocialBlockingListMenuItem = optionsMenu.findItem(R.id.fanboys_social_blocking_list);
- case R.id.easylist:
- // Toggle the EasyList status.
- currentWebView.enableBlocklist(NestedScrollWebView.EASYLIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYLIST));
+ // Update the staus of Fanboy's Social Blocking List.
+ fanboysSocialBlockingListMenuItem.setEnabled(!currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST));
- // Update the menu checkbox.
- menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYLIST));
+ // Reload the current WebView.
+ currentWebView.reload();
- // Reload the current WebView.
- currentWebView.reload();
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.fanboys_social_blocking_list) { // Fanboy's Social Blocking List.
+ // Toggle Fanboy's Social Blocking List status.
+ currentWebView.enableBlocklist(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST));
- // Consume the event.
- return true;
+ // Update the menu checkbox.
+ menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST));
- case R.id.easyprivacy:
- // Toggle the EasyPrivacy status.
- currentWebView.enableBlocklist(NestedScrollWebView.EASYPRIVACY, !currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYPRIVACY));
+ // Reload the current WebView.
+ currentWebView.reload();
- // Update the menu checkbox.
- menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYPRIVACY));
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.ultralist) { // UltraList.
+ // Toggle the UltraList status.
+ currentWebView.enableBlocklist(NestedScrollWebView.ULTRALIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRALIST));
- // Reload the current WebView.
- currentWebView.reload();
+ // Update the menu checkbox.
+ menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRALIST));
- // Consume the event.
- return true;
+ // Reload the current WebView.
+ currentWebView.reload();
- case R.id.fanboys_annoyance_list:
- // Toggle Fanboy's Annoyance List status.
- currentWebView.enableBlocklist(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST));
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.ultraprivacy) { // UltraPrivacy.
+ // Toggle the UltraPrivacy status.
+ currentWebView.enableBlocklist(NestedScrollWebView.ULTRAPRIVACY, !currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRAPRIVACY));
- // Update the menu checkbox.
- menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST));
+ // Update the menu checkbox.
+ menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRAPRIVACY));
- // Update the staus of Fanboy's Social Blocking List.
- MenuItem fanboysSocialBlockingListMenuItem = optionsMenu.findItem(R.id.fanboys_social_blocking_list);
- fanboysSocialBlockingListMenuItem.setEnabled(!currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST));
+ // Reload the current WebView.
+ currentWebView.reload();
- // Reload the current WebView.
- currentWebView.reload();
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.block_all_third_party_requests) { // Block all third-party requests.
+ //Toggle the third-party requests blocker status.
+ currentWebView.enableBlocklist(NestedScrollWebView.THIRD_PARTY_REQUESTS, !currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS));
- // Consume the event.
- return true;
+ // Update the menu checkbox.
+ menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS));
- case R.id.fanboys_social_blocking_list:
- // Toggle Fanboy's Social Blocking List status.
- currentWebView.enableBlocklist(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST));
+ // Reload the current WebView.
+ currentWebView.reload();
- // Update the menu checkbox.
- menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST));
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.proxy_none) { // Proxy - None.
+ // Update the proxy mode.
+ proxyMode = ProxyHelper.NONE;
- // Reload the current WebView.
- currentWebView.reload();
+ // Apply the proxy mode.
+ applyProxy(true);
- // Consume the event.
- return true;
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.proxy_tor) { // Proxy - Tor.
+ // Update the proxy mode.
+ proxyMode = ProxyHelper.TOR;
- case R.id.ultralist:
- // Toggle the UltraList status.
- currentWebView.enableBlocklist(NestedScrollWebView.ULTRALIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRALIST));
+ // Apply the proxy mode.
+ applyProxy(true);
- // Update the menu checkbox.
- menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRALIST));
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.proxy_i2p) { // Proxy - I2P.
+ // Update the proxy mode.
+ proxyMode = ProxyHelper.I2P;
- // Reload the current WebView.
- currentWebView.reload();
+ // Apply the proxy mode.
+ applyProxy(true);
- // Consume the event.
- return true;
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.proxy_custom) { // Proxy - Custom.
+ // Update the proxy mode.
+ proxyMode = ProxyHelper.CUSTOM;
- case R.id.ultraprivacy:
- // Toggle the UltraPrivacy status.
- currentWebView.enableBlocklist(NestedScrollWebView.ULTRAPRIVACY, !currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRAPRIVACY));
+ // Apply the proxy mode.
+ applyProxy(true);
- // Update the menu checkbox.
- menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRAPRIVACY));
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.user_agent_privacy_browser) { // User Agent - Privacy Browser.
+ // Update the user agent.
+ currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[0]);
- // Reload the current WebView.
- currentWebView.reload();
+ // Reload the current WebView.
+ currentWebView.reload();
- // Consume the event.
- return true;
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.user_agent_webview_default) { // User Agent - WebView Default.
+ // Update the user agent.
+ currentWebView.getSettings().setUserAgentString("");
- case R.id.block_all_third_party_requests:
- //Toggle the third-party requests blocker status.
- currentWebView.enableBlocklist(NestedScrollWebView.THIRD_PARTY_REQUESTS, !currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS));
+ // Reload the current WebView.
+ currentWebView.reload();
- // Update the menu checkbox.
- menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS));
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.user_agent_firefox_on_android) { // User Agent - Firefox on Android.
+ // Update the user agent.
+ currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[2]);
- // Reload the current WebView.
- currentWebView.reload();
+ // Reload the current WebView.
+ currentWebView.reload();
- // Consume the event.
- return true;
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.user_agent_chrome_on_android) { // User Agent - Chrome on Android.
+ // Update the user agent.
+ currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[3]);
- case R.id.proxy_none:
- // Update the proxy mode.
- proxyMode = ProxyHelper.NONE;
+ // Reload the current WebView.
+ currentWebView.reload();
- // Apply the proxy mode.
- applyProxy(true);
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.user_agent_safari_on_ios) { // User Agent - Safari on iOS.
+ // Update the user agent.
+ currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[4]);
- // Consume the event.
- return true;
+ // Reload the current WebView.
+ currentWebView.reload();
- case R.id.proxy_tor:
- // Update the proxy mode.
- proxyMode = ProxyHelper.TOR;
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.user_agent_firefox_on_linux) { // User Agent - Firefox on Linux.
+ // Update the user agent.
+ currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[5]);
- // Apply the proxy mode.
- applyProxy(true);
+ // Reload the current WebView.
+ currentWebView.reload();
- // Consume the event.
- return true;
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.user_agent_chromium_on_linux) { // User Agent - Chromium on Linux.
+ // Update the user agent.
+ currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[6]);
- case R.id.proxy_i2p:
- // Update the proxy mode.
- proxyMode = ProxyHelper.I2P;
+ // Reload the current WebView.
+ currentWebView.reload();
- // Apply the proxy mode.
- applyProxy(true);
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.user_agent_firefox_on_windows) { // User Agent - Firefox on Windows.
+ // Update the user agent.
+ currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[7]);
- // Consume the event.
- return true;
+ // Reload the current WebView.
+ currentWebView.reload();
- case R.id.proxy_custom:
- // Update the proxy mode.
- proxyMode = ProxyHelper.CUSTOM;
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.user_agent_chrome_on_windows) { // User Agent - Chrome on Windows.
+ // Update the user agent.
+ currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[8]);
- // Apply the proxy mode.
- applyProxy(true);
+ // Reload the current WebView.
+ currentWebView.reload();
- // Consume the event.
- return true;
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.user_agent_edge_on_windows) { // User Agent - Edge on Windows.
+ // Update the user agent.
+ currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[9]);
- case R.id.user_agent_privacy_browser:
- // Update the user agent.
- currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[0]);
+ // Reload the current WebView.
+ currentWebView.reload();
- // Reload the current WebView.
- currentWebView.reload();
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.user_agent_internet_explorer_on_windows) { // User Agent - Internet Explorer on Windows.
+ // Update the user agent.
+ currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[10]);
- // Consume the event.
- return true;
+ // Reload the current WebView.
+ currentWebView.reload();
- case R.id.user_agent_webview_default:
- // Update the user agent.
- currentWebView.getSettings().setUserAgentString("");
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.user_agent_safari_on_macos) { // User Agent - Safari on macOS.
+ // Update the user agent.
+ currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[11]);
- // Reload the current WebView.
- currentWebView.reload();
+ // Reload the current WebView.
+ currentWebView.reload();
- // Consume the event.
- return true;
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.user_agent_custom) { // User Agent - Custom.
+ // Update the user agent.
+ currentWebView.getSettings().setUserAgentString(sharedPreferences.getString("custom_user_agent", getString(R.string.custom_user_agent_default_value)));
- case R.id.user_agent_firefox_on_android:
- // Update the user agent.
- currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[2]);
+ // Reload the current WebView.
+ currentWebView.reload();
- // Reload the current WebView.
- currentWebView.reload();
-
- // Consume the event.
- return true;
-
- case R.id.user_agent_chrome_on_android:
- // Update the user agent.
- currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[3]);
-
- // Reload the current WebView.
- currentWebView.reload();
-
- // Consume the event.
- return true;
-
- case R.id.user_agent_safari_on_ios:
- // Update the user agent.
- currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[4]);
-
- // Reload the current WebView.
- currentWebView.reload();
-
- // Consume the event.
- return true;
-
- case R.id.user_agent_firefox_on_linux:
- // Update the user agent.
- currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[5]);
-
- // Reload the current WebView.
- currentWebView.reload();
-
- // Consume the event.
- return true;
-
- case R.id.user_agent_chromium_on_linux:
- // Update the user agent.
- currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[6]);
-
- // Reload the current WebView.
- currentWebView.reload();
-
- // Consume the event.
- return true;
-
- case R.id.user_agent_firefox_on_windows:
- // Update the user agent.
- currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[7]);
-
- // Reload the current WebView.
- currentWebView.reload();
-
- // Consume the event.
- return true;
-
- case R.id.user_agent_chrome_on_windows:
- // Update the user agent.
- currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[8]);
-
- // Reload the current WebView.
- currentWebView.reload();
-
- // Consume the event.
- return true;
-
- case R.id.user_agent_edge_on_windows:
- // Update the user agent.
- currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[9]);
-
- // Reload the current WebView.
- currentWebView.reload();
-
- // Consume the event.
- return true;
-
- case R.id.user_agent_internet_explorer_on_windows:
- // Update the user agent.
- currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[10]);
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.font_size) { // Font size.
+ // Instantiate the font size dialog.
+ DialogFragment fontSizeDialogFragment = FontSizeDialog.displayDialog(currentWebView.getSettings().getTextZoom());
- // Reload the current WebView.
- currentWebView.reload();
+ // Show the font size dialog.
+ fontSizeDialogFragment.show(getSupportFragmentManager(), getString(R.string.font_size));
- // Consume the event.
- return true;
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.swipe_to_refresh) { // Swipe to refresh.
+ // Toggle the stored status of swipe to refresh.
+ currentWebView.setSwipeToRefresh(!currentWebView.getSwipeToRefresh());
- case R.id.user_agent_safari_on_macos:
- // Update the user agent.
- currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[11]);
+ // Get a handle for the swipe refresh layout.
+ SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
- // Reload the current WebView.
- currentWebView.reload();
+ // Update the swipe refresh layout.
+ if (currentWebView.getSwipeToRefresh()) { // Swipe to refresh is enabled.
+ // Only enable the swipe refresh layout if the WebView is scrolled to the top. It is updated every time the scroll changes.
+ swipeRefreshLayout.setEnabled(currentWebView.getY() == 0);
+ } else { // Swipe to refresh is disabled.
+ // Disable the swipe refresh layout.
+ swipeRefreshLayout.setEnabled(false);
+ }
- // Consume the event.
- return true;
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.wide_viewport) { // Wide viewport.
+ // Toggle the viewport.
+ currentWebView.getSettings().setUseWideViewPort(!currentWebView.getSettings().getUseWideViewPort());
- case R.id.user_agent_custom:
- // Update the user agent.
- currentWebView.getSettings().setUserAgentString(sharedPreferences.getString("custom_user_agent", getString(R.string.custom_user_agent_default_value)));
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.display_images) { // Display images.
+ // Toggle the displaying of images.
+ if (currentWebView.getSettings().getLoadsImagesAutomatically()) { // Images are currently loaded automatically.
+ // Disable loading of images.
+ currentWebView.getSettings().setLoadsImagesAutomatically(false);
- // Reload the current WebView.
+ // Reload the website to remove existing images.
currentWebView.reload();
+ } else { // Images are not currently loaded automatically.
+ // Enable loading of images. Missing images will be loaded without the need for a reload.
+ currentWebView.getSettings().setLoadsImagesAutomatically(true);
+ }
- // Consume the event.
- return true;
-
- case R.id.font_size:
- // Instantiate the font size dialog.
- DialogFragment fontSizeDialogFragment = FontSizeDialog.displayDialog(currentWebView.getSettings().getTextZoom());
-
- // Show the font size dialog.
- fontSizeDialogFragment.show(getSupportFragmentManager(), getString(R.string.font_size));
-
- // Consume the event.
- return true;
-
- case R.id.swipe_to_refresh:
- // Toggle the stored status of swipe to refresh.
- currentWebView.setSwipeToRefresh(!currentWebView.getSwipeToRefresh());
-
- // Get a handle for the swipe refresh layout.
- SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
-
- // Update the swipe refresh layout.
- if (currentWebView.getSwipeToRefresh()) { // Swipe to refresh is enabled.
- // Only enable the swipe refresh layout if the WebView is scrolled to the top. It is updated every time the scroll changes.
- swipeRefreshLayout.setEnabled(currentWebView.getY() == 0);
- } else { // Swipe to refresh is disabled.
- // Disable the swipe refresh layout.
- swipeRefreshLayout.setEnabled(false);
- }
-
- // Consume the event.
- return true;
-
- case R.id.wide_viewport:
- // Toggle the viewport.
- currentWebView.getSettings().setUseWideViewPort(!currentWebView.getSettings().getUseWideViewPort());
-
- // Consume the event.
- return true;
-
- case R.id.display_images:
- if (currentWebView.getSettings().getLoadsImagesAutomatically()) { // Images are currently loaded automatically.
- // Disable loading of images.
- currentWebView.getSettings().setLoadsImagesAutomatically(false);
-
- // Reload the website to remove existing images.
- currentWebView.reload();
- } else { // Images are not currently loaded automatically.
- // Enable loading of images. Missing images will be loaded without the need for a reload.
- currentWebView.getSettings().setLoadsImagesAutomatically(true);
- }
-
- // Consume the event.
- return true;
-
- case R.id.night_mode:
- // Toggle night mode.
- currentWebView.setNightMode(!currentWebView.getNightMode());
-
- // Enable or disable JavaScript according to night mode, the global preference, and any domain settings.
- if (currentWebView.getNightMode()) { // Night mode is enabled, which requires JavaScript.
- // Enable JavaScript.
- currentWebView.getSettings().setJavaScriptEnabled(true);
- } else if (currentWebView.getDomainSettingsApplied()) { // Night mode is disabled and domain settings are applied. Set JavaScript according to the domain settings.
- // Apply the JavaScript preference that was stored the last time domain settings were loaded.
- currentWebView.getSettings().setJavaScriptEnabled(currentWebView.getDomainSettingsJavaScriptEnabled());
- } else { // Night mode is disabled and domain settings are not applied. Set JavaScript according to the global preference.
- // Apply the JavaScript preference.
- currentWebView.getSettings().setJavaScriptEnabled(sharedPreferences.getBoolean("javascript", false));
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.dark_webview) { // Dark WebView.
+ // Check to see if dark WebView is supported by this WebView.
+ if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
+ // Toggle the dark WebView setting.
+ if (WebSettingsCompat.getForceDark(currentWebView.getSettings()) == WebSettingsCompat.FORCE_DARK_ON) { // Dark WebView is currently enabled.
+ // Turn off dark WebView.
+ WebSettingsCompat.setForceDark(currentWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+ } else { // Dark WebView is currently disabled.
+ // Turn on dark WebView.
+ WebSettingsCompat.setForceDark(currentWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
}
+ }
- // Update the privacy icons.
- updatePrivacyIcons(false);
-
- // Reload the website.
- currentWebView.reload();
-
- // Consume the event.
- return true;
-
- case R.id.find_on_page:
- // Get a handle for the views.
- Toolbar toolbar = findViewById(R.id.toolbar);
- LinearLayout findOnPageLinearLayout = findViewById(R.id.find_on_page_linearlayout);
- EditText findOnPageEditText = findViewById(R.id.find_on_page_edittext);
-
- // Set the minimum height of the find on page linear layout to match the toolbar.
- findOnPageLinearLayout.setMinimumHeight(toolbar.getHeight());
-
- // Hide the toolbar.
- toolbar.setVisibility(View.GONE);
-
- // Show the find on page linear layout.
- findOnPageLinearLayout.setVisibility(View.VISIBLE);
-
- // Display the keyboard. The app must wait 200 ms before running the command to work around a bug in Android.
- // http://stackoverflow.com/questions/5520085/android-show-softkeyboard-with-showsoftinput-is-not-working
- findOnPageEditText.postDelayed(() -> {
- // Set the focus on `findOnPageEditText`.
- findOnPageEditText.requestFocus();
-
- // Get a handle for the input method manager.
- InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
-
- // Remove the lint warning below that the input method manager might be null.
- assert inputMethodManager != null;
-
- // Display the keyboard. `0` sets no input flags.
- inputMethodManager.showSoftInput(findOnPageEditText, 0);
- }, 200);
-
- // Consume the event.
- return true;
-
- case R.id.print:
- // Get a print manager instance.
- PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE);
-
- // Remove the lint error below that print manager might be null.
- assert printManager != null;
-
- // Create a print document adapter from the current WebView.
- PrintDocumentAdapter printDocumentAdapter = currentWebView.createPrintDocumentAdapter();
-
- // Print the document.
- printManager.print(getString(R.string.privacy_browser_web_page), printDocumentAdapter, null);
-
- // Consume the event.
- return true;
-
- case R.id.save_url:
- // Prepare the save dialog. The dialog will be displayed once the file size and the content disposition have been acquired.
- new PrepareSaveDialog(this, this, getSupportFragmentManager(), StoragePermissionDialog.SAVE_URL, currentWebView.getSettings().getUserAgentString(),
- currentWebView.getAcceptFirstPartyCookies()).execute(currentWebView.getCurrentUrl());
-
- // Consume the event.
- return true;
-
- case R.id.save_as_archive:
- // Prepare the save dialog. The dialog will be displayed once the file size and the content disposition have been acquired.
- new PrepareSaveDialog(this, this, getSupportFragmentManager(), StoragePermissionDialog.SAVE_AS_ARCHIVE, currentWebView.getSettings().getUserAgentString(),
- currentWebView.getAcceptFirstPartyCookies()).execute(currentWebView.getCurrentUrl());
-
- // Consume the event.
- return true;
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.find_on_page) { // Find on page.
+ // Get a handle for the views.
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ LinearLayout findOnPageLinearLayout = findViewById(R.id.find_on_page_linearlayout);
+ EditText findOnPageEditText = findViewById(R.id.find_on_page_edittext);
- case R.id.save_as_image:
- // Prepare the save dialog. The dialog will be displayed once the file size adn the content disposition have been acquired.
- new PrepareSaveDialog(this, this, getSupportFragmentManager(), StoragePermissionDialog.SAVE_AS_IMAGE, currentWebView.getSettings().getUserAgentString(),
- currentWebView.getAcceptFirstPartyCookies()).execute(currentWebView.getCurrentUrl());
+ // Set the minimum height of the find on page linear layout to match the toolbar.
+ findOnPageLinearLayout.setMinimumHeight(toolbar.getHeight());
- // Consume the event.
- return true;
+ // Hide the toolbar.
+ toolbar.setVisibility(View.GONE);
- case R.id.add_to_homescreen:
- // Instantiate the create home screen shortcut dialog.
- DialogFragment createHomeScreenShortcutDialogFragment = CreateHomeScreenShortcutDialog.createDialog(currentWebView.getTitle(), currentWebView.getUrl(),
- currentWebView.getFavoriteOrDefaultIcon());
+ // Show the find on page linear layout.
+ findOnPageLinearLayout.setVisibility(View.VISIBLE);
- // Show the create home screen shortcut dialog.
- createHomeScreenShortcutDialogFragment.show(getSupportFragmentManager(), getString(R.string.create_shortcut));
+ // Display the keyboard. The app must wait 200 ms before running the command to work around a bug in Android.
+ // http://stackoverflow.com/questions/5520085/android-show-softkeyboard-with-showsoftinput-is-not-working
+ findOnPageEditText.postDelayed(() -> {
+ // Set the focus on the find on page edit text.
+ findOnPageEditText.requestFocus();
- // Consume the event.
- return true;
+ // Get a handle for the input method manager.
+ InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
- case R.id.view_source:
- // Create an intent to launch the view source activity.
- Intent viewSourceIntent = new Intent(this, ViewSourceActivity.class);
+ // Remove the lint warning below that the input method manager might be null.
+ assert inputMethodManager != null;
- // Add the variables to the intent.
- viewSourceIntent.putExtra("user_agent", currentWebView.getSettings().getUserAgentString());
- viewSourceIntent.putExtra("current_url", currentWebView.getUrl());
+ // Display the keyboard. `0` sets no input flags.
+ inputMethodManager.showSoftInput(findOnPageEditText, 0);
+ }, 200);
- // Make it so.
- startActivity(viewSourceIntent);
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.print) { // Print.
+ // Get a print manager instance.
+ PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE);
- // Consume the event.
- return true;
+ // Remove the lint error below that print manager might be null.
+ assert printManager != null;
- case R.id.share_url:
- // Setup the share string.
- String shareString = currentWebView.getTitle() + " – " + currentWebView.getUrl();
+ // Create a print document adapter from the current WebView.
+ PrintDocumentAdapter printDocumentAdapter = currentWebView.createPrintDocumentAdapter();
- // Create the share intent.
- Intent shareIntent = new Intent(Intent.ACTION_SEND);
- shareIntent.putExtra(Intent.EXTRA_TEXT, shareString);
- shareIntent.setType("text/plain");
+ // Print the document.
+ printManager.print(getString(R.string.privacy_browser_web_page), printDocumentAdapter, null);
- // Make it so.
- startActivity(Intent.createChooser(shareIntent, getString(R.string.share_url)));
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.save_url) { // Save URL.
+ // Prepare the save dialog. The dialog will be displayed once the file size and the content disposition have been acquired.
+ new PrepareSaveDialog(this, this, getSupportFragmentManager(), StoragePermissionDialog.SAVE_URL, currentWebView.getSettings().getUserAgentString(),
+ currentWebView.getAcceptFirstPartyCookies()).execute(currentWebView.getCurrentUrl());
- // Consume the event.
- return true;
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.save_archive) { // Save archive.
+ // Instantiate the save dialog.
+ DialogFragment saveArchiveFragment = SaveWebpageDialog.saveWebpage(StoragePermissionDialog.SAVE_ARCHIVE, null, null, getString(R.string.webpage_mht), null,
+ false);
- case R.id.open_with_app:
- // Open the URL with an outside app.
- openWithApp(currentWebView.getUrl());
+ // Show the save dialog. It must be named `save_dialog` so that the file picker can update the file name.
+ saveArchiveFragment.show(getSupportFragmentManager(), getString(R.string.save_dialog));
- // Consume the event.
- return true;
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.save_image) { // Save image.
+ // Instantiate the save dialog.
+ DialogFragment saveImageFragment = SaveWebpageDialog.saveWebpage(StoragePermissionDialog.SAVE_IMAGE, null, null, getString(R.string.webpage_png), null,
+ false);
- case R.id.open_with_browser:
- // Open the URL with an outside browser.
- openWithBrowser(currentWebView.getUrl());
+ // Show the save dialog. It must be named `save_dialog` so that the file picker can update the file name.
+ saveImageFragment.show(getSupportFragmentManager(), getString(R.string.save_dialog));
- // Consume the event.
- return true;
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.add_to_homescreen) { // Add to homescreen.
+ // Instantiate the create home screen shortcut dialog.
+ DialogFragment createHomeScreenShortcutDialogFragment = CreateHomeScreenShortcutDialog.createDialog(currentWebView.getTitle(), currentWebView.getUrl(),
+ currentWebView.getFavoriteOrDefaultIcon());
- 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);
- }
+ // Show the create home screen shortcut dialog.
+ createHomeScreenShortcutDialogFragment.show(getSupportFragmentManager(), getString(R.string.create_shortcut));
- // 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());
- }
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.view_source) { // View source.
+ // Create an intent to launch the view source activity.
+ Intent viewSourceIntent = new Intent(this, ViewSourceActivity.class);
- // 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);
- }
+ // Add the variables to the intent.
+ viewSourceIntent.putExtra("user_agent", currentWebView.getSettings().getUserAgentString());
+ viewSourceIntent.putExtra("current_url", currentWebView.getUrl());
- // 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(viewSourceIntent);
- // Make it so.
- startActivity(domainsIntent);
- }
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.share_url) { // Share URL.
+ // Setup the share string.
+ String shareString = currentWebView.getTitle() + " – " + currentWebView.getUrl();
- // Consume the event.
- return true;
+ // Create the share intent.
+ Intent shareIntent = new Intent(Intent.ACTION_SEND);
- case R.id.ad_consent:
- // Instantiate the ad consent dialog.
- DialogFragment adConsentDialogFragment = new AdConsentDialog();
+ // Add the share string to the intent.
+ shareIntent.putExtra(Intent.EXTRA_TEXT, shareString);
- // Display the ad consent dialog.
- adConsentDialogFragment.show(getSupportFragmentManager(), getString(R.string.ad_consent));
+ // Set the MIME type.
+ shareIntent.setType("text/plain");
- // Consume the event.
- return true;
+ // Set the intent to open in a new task.
+ shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- default:
- // Don't consume the event.
- return super.onOptionsItemSelected(menuItem);
- }
- }
+ // Make it so.
+ startActivity(Intent.createChooser(shareIntent, getString(R.string.share_url)));
- // removeAllCookies is deprecated, but it is required for API < 21.
- @Override
- public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) {
- // Get the menu item ID.
- int menuItemId = menuItem.getItemId();
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.open_with_app) { // Open with app.
+ // Open the URL with an outside app.
+ openWithApp(currentWebView.getUrl());
- // Get a handle for the shared preferences.
- SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.open_with_browser) { // Open with browser.
+ // Open the URL with an outside browser.
+ openWithBrowser(currentWebView.getUrl());
- // Run the commands that correspond to the selected menu item.
- switch (menuItemId) {
- case R.id.clear_and_exit:
- // Clear and exit Privacy Browser.
- clearAndExit();
- break;
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.add_or_edit_domain) { // Add or edit domain.
+ // Check if domain settings currently exist.
+ if (currentWebView.getDomainSettingsApplied()) { // Edit the current domain settings.
+ // Reapply the domain settings on returning to `MainWebViewActivity`.
+ reapplyDomainSettingsOnRestart = true;
- case R.id.home:
- // Load the homepage.
- loadUrl(currentWebView, sharedPreferences.getString("homepage", getString(R.string.homepage_default_value)));
- break;
+ // Create an intent to launch the domains activity.
+ Intent domainsIntent = new Intent(this, DomainsActivity.class);
- case R.id.back:
- if (currentWebView.canGoBack()) {
- // Get the current web back forward list.
- WebBackForwardList webBackForwardList = currentWebView.copyBackForwardList();
+ // 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 previous entry URL.
- String previousUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() - 1).getUrl();
+ // Get the current certificate.
+ SslCertificate sslCertificate = currentWebView.getCertificate();
- // Apply the domain settings.
- applyDomainSettings(currentWebView, previousUrl, false, false);
+ // 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();
- // Load the previous website in the history.
- currentWebView.goBack();
+ // 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);
}
- break;
-
- case R.id.forward:
- if (currentWebView.canGoForward()) {
- // Get the current web back forward list.
- WebBackForwardList webBackForwardList = currentWebView.copyBackForwardList();
-
- // Get the next entry URL.
- String nextUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() + 1).getUrl();
- // Apply the domain settings.
- applyDomainSettings(currentWebView, nextUrl, false, false);
-
- // Load the next website in the history.
- currentWebView.goForward();
+ // 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());
}
- break;
-
- case R.id.history:
- // Instantiate the URL history dialog.
- DialogFragment urlHistoryDialogFragment = UrlHistoryDialog.loadBackForwardList(currentWebView.getWebViewFragmentId());
-
- // Show the URL history dialog.
- 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();
-
- // Create an intent to launch the Requests activity.
- Intent requestsIntent = new Intent(this, RequestsActivity.class);
-
- // Add the block third-party requests status to the intent.
- requestsIntent.putExtra("block_all_third_party_requests", currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS));
// Make it so.
- startActivity(requestsIntent);
- break;
-
- case R.id.downloads:
- // Launch the system Download Manager.
- Intent downloadManagerIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
+ startActivity(domainsIntent);
+ } else { // Add a new domain.
+ // Apply the new domain settings on returning to `MainWebViewActivity`.
+ reapplyDomainSettingsOnRestart = true;
- // 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);
+ // Get the current domain
+ Uri currentUri = Uri.parse(currentWebView.getUrl());
+ String currentDomain = currentUri.getHost();
- // Make it so.
- startActivity(downloadManagerIntent);
- break;
+ // 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);
- case R.id.domains:
- // Set the flag to reapply the domain settings on restart when returning from Domain Settings.
- reapplyDomainSettingsOnRestart = true;
+ // Create the domain and store the database ID.
+ int newDomainDatabaseId = domainsDatabaseHelper.addDomain(currentDomain);
- // Launch the domains activity.
+ // 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.
// Make it so.
startActivity(domainsIntent);
- break;
+ }
- case R.id.settings:
- // Set the flag to reapply app settings on restart when returning from Settings.
- reapplyAppSettingsOnRestart = true;
+ // Consume the event.
+ return true;
+ } else if (menuItemId == R.id.ad_consent) { // Ad consent.
+ // Instantiate the ad consent dialog.
+ DialogFragment adConsentDialogFragment = new AdConsentDialog();
- // Set the flag to reapply the domain settings on restart when returning from Settings.
- reapplyDomainSettingsOnRestart = true;
+ // Display the ad consent dialog.
+ adConsentDialogFragment.show(getSupportFragmentManager(), getString(R.string.ad_consent));
- // Launch the settings activity.
- Intent settingsIntent = new Intent(this, SettingsActivity.class);
- startActivity(settingsIntent);
- break;
+ // Consume the event.
+ return true;
+ } else { // There is no match with the options menu. Pass the event up to the parent method.
+ // Don't consume the event.
+ return super.onOptionsItemSelected(menuItem);
+ }
+ }
- case R.id.import_export:
- // Launch the import/export activity.
- Intent importExportIntent = new Intent (this, ImportExportActivity.class);
- startActivity(importExportIntent);
- break;
+ // removeAllCookies is deprecated, but it is required for API < 21.
+ @Override
+ public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) {
+ // Get a handle for the shared preferences.
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
- case R.id.logcat:
- // Launch the logcat activity.
- Intent logcatIntent = new Intent(this, LogcatActivity.class);
- startActivity(logcatIntent);
- break;
+ // Get the menu item ID.
+ int menuItemId = menuItem.getItemId();
- case R.id.guide:
- // Launch `GuideActivity`.
- Intent guideIntent = new Intent(this, GuideActivity.class);
- startActivity(guideIntent);
- break;
+ // Run the commands that correspond to the selected menu item.
+ if (menuItemId == R.id.clear_and_exit) { // Clear and exit.
+ // Clear and exit Privacy Browser.
+ clearAndExit();
+ } else if (menuItemId == R.id.home) { // Home.
+ // Load the homepage.
+ loadUrl(currentWebView, sharedPreferences.getString("homepage", getString(R.string.homepage_default_value)));
+ } else if (menuItemId == R.id.back) { // Back.
+ // Check if the WebView can go back.
+ if (currentWebView.canGoBack()) {
+ // Get the current web back forward list.
+ WebBackForwardList webBackForwardList = currentWebView.copyBackForwardList();
+
+ // Get the previous entry URL.
+ String previousUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() - 1).getUrl();
- case R.id.about:
- // Create an intent to launch the about activity.
- Intent aboutIntent = new Intent(this, AboutActivity.class);
+ // Apply the domain settings.
+ applyDomainSettings(currentWebView, previousUrl, false, false, false);
- // Create a string array for the blocklist versions.
- String[] blocklistVersions = new String[] {easyList.get(0).get(0)[0], easyPrivacy.get(0).get(0)[0], fanboysAnnoyanceList.get(0).get(0)[0], fanboysSocialList.get(0).get(0)[0],
- ultraList.get(0).get(0)[0], ultraPrivacy.get(0).get(0)[0]};
+ // Load the previous website in the history.
+ currentWebView.goBack();
+ }
+ } else if (menuItemId == R.id.forward) { // Forward.
+ // Check if the WebView can go forward.
+ if (currentWebView.canGoForward()) {
+ // Get the current web back forward list.
+ WebBackForwardList webBackForwardList = currentWebView.copyBackForwardList();
- // Add the blocklist versions to the intent.
- aboutIntent.putExtra("blocklist_versions", blocklistVersions);
+ // Get the next entry URL.
+ String nextUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() + 1).getUrl();
- // Make it so.
- startActivity(aboutIntent);
- break;
- }
+ // Apply the domain settings.
+ applyDomainSettings(currentWebView, nextUrl, false, false, false);
+
+ // Load the next website in the history.
+ currentWebView.goForward();
+ }
+ } else if (menuItemId == R.id.history) { // History.
+ // Instantiate the URL history dialog.
+ DialogFragment urlHistoryDialogFragment = UrlHistoryDialog.loadBackForwardList(currentWebView.getWebViewFragmentId());
+
+ // Show the URL history dialog.
+ urlHistoryDialogFragment.show(getSupportFragmentManager(), getString(R.string.history));
+ } else if (menuItemId == R.id.open) { // Open.
+ // Instantiate the open file dialog.
+ DialogFragment openDialogFragment = new OpenDialog();
+
+ // Show the open file dialog.
+ openDialogFragment.show(getSupportFragmentManager(), getString(R.string.open));
+ } else if (menuItemId == R.id.requests) { // Requests.
+ // Populate the resource requests.
+ RequestsActivity.resourceRequests = currentWebView.getResourceRequests();
+
+ // Create an intent to launch the Requests activity.
+ Intent requestsIntent = new Intent(this, RequestsActivity.class);
+
+ // Add the block third-party requests status to the intent.
+ requestsIntent.putExtra("block_all_third_party_requests", currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS));
+
+ // Make it so.
+ startActivity(requestsIntent);
+ } else if (menuItemId == R.id.downloads) { // Downloads.
+ // Launch the system Download Manager.
+ Intent downloadManagerIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
+
+ // 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);
+ } else if (menuItemId == R.id.domains) { // Domains.
+ // Set the flag to reapply the domain settings on restart when returning from Domain Settings.
+ reapplyDomainSettingsOnRestart = true;
+
+ // Launch the domains activity.
+ Intent domainsIntent = new Intent(this, DomainsActivity.class);
+
+ // Add the extra information to the intent.
+ 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 if (menuItemId == R.id.settings) { // Settings.
+ // Set the flag to reapply app settings on restart when returning from Settings.
+ reapplyAppSettingsOnRestart = true;
+
+ // Set the flag to reapply the domain settings on restart when returning from Settings.
+ reapplyDomainSettingsOnRestart = true;
+
+ // Launch the settings activity.
+ Intent settingsIntent = new Intent(this, SettingsActivity.class);
+ startActivity(settingsIntent);
+ } else if (menuItemId == R.id.import_export) { // Import/Export.
+ // Create an intent to launch the import/export activity.
+ Intent importExportIntent = new Intent(this, ImportExportActivity.class);
+
+ // Make it so.
+ startActivity(importExportIntent);
+ } else if (menuItemId == R.id.logcat) { // Logcat.
+ // Create an intent to launch the logcat activity.
+ Intent logcatIntent = new Intent(this, LogcatActivity.class);
+
+ // Make it so.
+ startActivity(logcatIntent);
+ } else if (menuItemId == R.id.guide) { // Guide.
+ // Create an intent to launch the guide activity.
+ Intent guideIntent = new Intent(this, GuideActivity.class);
+
+ // Make it so.
+ startActivity(guideIntent);
+ } else if (menuItemId == R.id.about) { // About
+ // Create an intent to launch the about activity.
+ Intent aboutIntent = new Intent(this, AboutActivity.class);
+
+ // Create a string array for the blocklist versions.
+ String[] blocklistVersions = new String[]{easyList.get(0).get(0)[0], easyPrivacy.get(0).get(0)[0], fanboysAnnoyanceList.get(0).get(0)[0], fanboysSocialList.get(0).get(0)[0],
+ ultraList.get(0).get(0)[0], ultraPrivacy.get(0).get(0)[0]};
- // Get a handle for the drawer layout.
- DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
+ // Add the blocklist versions to the intent.
+ aboutIntent.putExtra("blocklist_versions", blocklistVersions);
+
+ // Make it so.
+ startActivity(aboutIntent);
+ }
// Close the navigation drawer.
drawerLayout.closeDrawer(GravityCompat.START);
@Override
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
- // Store the hit test result.
+ // Get the hit test result.
final WebView.HitTestResult hitTestResult = currentWebView.getHitTestResult();
// Define the URL strings.
// Get the image URL.
imageUrl = hitTestResult.getExtra();
- // Set the image URL as the title of the context menu.
- menu.setHeaderTitle(imageUrl);
+ // Remove the incorrect lint warning below that the image URL might be null.
+ assert imageUrl != null;
+
+ // Set the context menu title.
+ if (imageUrl.startsWith("data:")) { // The image data is contained in within the URL, making it exceedingly long.
+ // Truncate the image URL before making it the title.
+ menu.setHeaderTitle(imageUrl.substring(0, 100));
+ } else { // The image URL does not contain the full image data.
+ // Set the image URL as the title of the context menu.
+ menu.setHeaderTitle(imageUrl);
+ }
// Add an Open in New Tab entry.
menu.add(R.string.open_image_in_new_tab).setOnMenuItemClickListener((MenuItem item) -> {
// `FLAG_ACTIVITY_NEW_TASK` opens the email program in a new task instead as part of Privacy Browser.
emailIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- // Make it so.
- startActivity(emailIntent);
+ try {
+ // Make it so.
+ startActivity(emailIntent);
+ } catch (ActivityNotFoundException exception) {
+ // Display a snackbar.
+ Snackbar.make(currentWebView, getString(R.string.error) + " " + exception, Snackbar.LENGTH_INDEFINITE).show();
+ }
// Consume the event.
return true;
@Override
public void onSaveBookmarkFolder(DialogFragment dialogFragment, int selectedFolderDatabaseId, Bitmap favoriteIconBitmap) {
+ // Remove the incorrect lint warning below that the dialog fragment might be null.
+ assert dialogFragment != null;
+
// 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`.
+ // Get handles for the views from the dialog.
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);
bookmarksCursorAdapter.changeCursor(bookmarksCursor);
}
- // Override `onBackPressed` to handle the navigation drawer and and the WebViews.
+ // 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);
-
+ // Check the different options for processing `back`.
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);
-
- // Load the new folder.
- loadBookmarksFolder();
- }
+ // close the bookmarks drawer.
+ drawerLayout.closeDrawer(GravityCompat.END);
} else if (displayingFullScreenVideo) { // A full screen video is shown.
// Get a handle for the layouts.
FrameLayout rootFrameLayout = findViewById(R.id.root_framelayout);
String previousUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() - 1).getUrl();
// Apply the domain settings.
- applyDomainSettings(currentWebView, previousUrl, false, false);
+ applyDomainSettings(currentWebView, previousUrl, false, false, false);
// Go back.
currentWebView.goBack();
// Sanitize the URL.
url = sanitizeUrl(url);
- // Apply the domain settings.
- applyDomainSettings(nestedScrollWebView, url, true, false);
-
- // Load the URL.
- nestedScrollWebView.loadUrl(url, customHeaders);
+ // Apply the domain settings and load the URL.
+ applyDomainSettings(nestedScrollWebView, url, true, false, true);
}
public void findPreviousOnPage(View view) {
@Override
public void onApplyNewFontSize(DialogFragment dialogFragment) {
+ // Remove the incorrect lint warning below that the dialog fragment might be null.
+ assert dialogFragment != null;
+
// Get the dialog.
Dialog dialog = dialogFragment.getDialog();
// Get the file path string.
openFilePath = fileNameEditText.getText().toString();
+ // Apply the domain settings. This resets the favorite icon and removes any domain settings.
+ applyDomainSettings(currentWebView, "file://" + openFilePath, true, false, false);
+
// 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.
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);
+ ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, StoragePermissionDialog.OPEN);
}
}
}
}
@Override
- public void onSaveWebpage(int saveType, DialogFragment dialogFragment) {
+ public void onSaveWebpage(int saveType, String originalUrlString, DialogFragment dialogFragment) {
// Get the dialog.
Dialog dialog = dialogFragment.getDialog();
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();
+ // Store the URL.
+ if ((originalUrlString != null) && originalUrlString.startsWith("data:")) {
+ // Save the original URL.
+ saveWebpageUrl = originalUrlString;
+ } else {
+ // Get the URL from the edit text, which may have been modified.
+ saveWebpageUrl = urlEditText.getText().toString();
+ }
+
+ // Get the file path from the edit text.
saveWebpageFilePath = fileNameEditText.getText().toString();
// Check to see if the storage permission is needed.
new SaveUrl(this, this, saveWebpageFilePath, currentWebView.getSettings().getUserAgentString(), currentWebView.getAcceptFirstPartyCookies()).execute(saveWebpageUrl);
break;
- case StoragePermissionDialog.SAVE_AS_ARCHIVE:
+ case StoragePermissionDialog.SAVE_ARCHIVE:
// Save the webpage archive.
- currentWebView.saveWebArchive(saveWebpageFilePath);
+ saveWebpageArchive(saveWebpageFilePath);
break;
- case StoragePermissionDialog.SAVE_AS_IMAGE:
+ case StoragePermissionDialog.SAVE_IMAGE:
// Save the webpage image.
- new SaveWebpageImage(this, currentWebView).execute(saveWebpageFilePath);
+ new SaveWebpageImage(this, this, saveWebpageFilePath, currentWebView).execute();
break;
}
+
+ // Reset the strings.
+ saveWebpageUrl = "";
+ saveWebpageFilePath = "";
} else { // The storage permission has not been granted.
// Get the external private directory file.
File externalPrivateDirectoryFile = getExternalFilesDir(null);
new SaveUrl(this, this, saveWebpageFilePath, currentWebView.getSettings().getUserAgentString(), currentWebView.getAcceptFirstPartyCookies()).execute(saveWebpageUrl);
break;
- case StoragePermissionDialog.SAVE_AS_ARCHIVE:
+ case StoragePermissionDialog.SAVE_ARCHIVE:
// Save the webpage archive.
- currentWebView.saveWebArchive(saveWebpageFilePath);
- break;
-
- case StoragePermissionDialog.SAVE_AS_IMAGE:
- // Save the webpage image.
- new SaveWebpageImage(this, currentWebView).execute(saveWebpageFilePath);
+ saveWebpageArchive(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 = 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.
- 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(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() {
- // Initialize the app if this is the first run. This is done here instead of in `onCreate()` to shorten the time that an unthemed background is displayed on app startup.
- if (webViewDefaultUserAgent == null) {
- initializeApp();
- }
-
- // Get a handle for the shared preferences.
- SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
-
- // Store the values from the shared preferences in variables.
- incognitoModeEnabled = sharedPreferences.getBoolean("incognito_mode", false);
- boolean doNotTrackEnabled = sharedPreferences.getBoolean("do_not_track", false);
- sanitizeGoogleAnalytics = sharedPreferences.getBoolean("google_analytics", true);
- sanitizeFacebookClickIds = sharedPreferences.getBoolean("facebook_click_ids", true);
- sanitizeTwitterAmpRedirects = sharedPreferences.getBoolean("twitter_amp_redirects", true);
- 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 a handle for the app compat delegate.
- AppCompatDelegate appCompatDelegate = getDelegate();
-
- // Get handles for the views that need to be modified.
- FrameLayout rootFrameLayout = findViewById(R.id.root_framelayout);
- AppBarLayout appBarLayout = findViewById(R.id.appbar_layout);
- ActionBar actionBar = appCompatDelegate.getSupportActionBar();
- Toolbar toolbar = findViewById(R.id.toolbar);
- LinearLayout findOnPageLinearLayout = findViewById(R.id.find_on_page_linearlayout);
- LinearLayout tabsLinearLayout = findViewById(R.id.tabs_linearlayout);
- SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
-
- // Remove the incorrect lint warning below that the action bar might be null.
- assert actionBar != null;
-
- // Apply the proxy.
- applyProxy(false);
-
- // Set Do Not Track status.
- if (doNotTrackEnabled) {
- customHeaders.put("DNT", "1");
- } else {
- customHeaders.remove("DNT");
- }
-
- // Get the current layout parameters. Using coordinator layout parameters allows the `setBehavior()` command and using app bar layout parameters allows the `setScrollFlags()` command.
- CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams();
- AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) toolbar.getLayoutParams();
- AppBarLayout.LayoutParams findOnPageLayoutParams = (AppBarLayout.LayoutParams) findOnPageLinearLayout.getLayoutParams();
- AppBarLayout.LayoutParams tabsLayoutParams = (AppBarLayout.LayoutParams) tabsLinearLayout.getLayoutParams();
-
- // Add the scrolling behavior to the layout parameters.
- if (scrollAppBar) {
- // Enable scrolling of the app bar.
- swipeRefreshLayoutParams.setBehavior(new AppBarLayout.ScrollingViewBehavior());
- toolbarLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP);
- findOnPageLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP);
- tabsLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP);
- } else {
- // Disable scrolling of the app bar.
- swipeRefreshLayoutParams.setBehavior(null);
- toolbarLayoutParams.setScrollFlags(0);
- findOnPageLayoutParams.setScrollFlags(0);
- tabsLayoutParams.setScrollFlags(0);
-
- // Expand the app bar if it is currently collapsed.
- appBarLayout.setExpanded(true);
- }
-
- // Apply the modified layout parameters.
- swipeRefreshLayout.setLayoutParams(swipeRefreshLayoutParams);
- toolbar.setLayoutParams(toolbarLayoutParams);
- findOnPageLinearLayout.setLayoutParams(findOnPageLayoutParams);
- tabsLinearLayout.setLayoutParams(tabsLayoutParams);
-
- // Set the app bar scrolling for each WebView.
- 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();
+ case StoragePermissionDialog.SAVE_IMAGE:
+ // Save the webpage image.
+ new SaveWebpageImage(this, this, saveWebpageFilePath, currentWebView).execute();
+ break;
+ }
- // Only modify 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);
+ // Reset the strings.
+ saveWebpageUrl = "";
+ saveWebpageFilePath = "";
+ } 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(saveType);
- // Set the app bar scrolling.
- nestedScrollWebView.setNestedScrollingEnabled(scrollAppBar);
+ // 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 according to the save type. The URL will be saved when it finishes.
+ ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, saveType);
+ }
}
}
+ }
- // Update the full screen browsing mode settings.
- if (fullScreenBrowsingModeEnabled && inFullScreenBrowsingMode) { // Privacy Browser is currently in full screen browsing mode.
- // Update the visibility of the app bar, which might have changed in the settings.
- if (hideAppBar) {
- // Hide the tab linear layout.
- tabsLinearLayout.setVisibility(View.GONE);
-
- // Hide the action bar.
- actionBar.hide();
- } else {
- // Show the tab linear layout.
- tabsLinearLayout.setVisibility(View.VISIBLE);
-
- // Show the action bar.
- actionBar.show();
- }
+ @Override
+ public void onCloseStoragePermissionDialog(int requestType) {
+ // Request the write external storage permission according to the request type. The file will be opened when it finishes.
+ ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, requestType);
- // Hide the banner ad in the free flavor.
- if (BuildConfig.FLAVOR.contentEquals("free")) {
- AdHelper.hideAd(findViewById(R.id.adview));
- }
+ }
- /* 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 { // Privacy Browser is not in full screen browsing mode.
- // Reset the full screen tracker, which could be true if Privacy Browser was in full screen mode before entering settings and full screen browsing was disabled.
- inFullScreenBrowsingMode = false;
+ @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 StoragePermissionDialog.OPEN:
+ // 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();
+ }
+ break;
- // Show the tab linear layout.
- tabsLinearLayout.setVisibility(View.VISIBLE);
+ case StoragePermissionDialog.SAVE_URL:
+ // 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();
+ }
+ break;
- // Show the action bar.
- actionBar.show();
+ case StoragePermissionDialog.SAVE_ARCHIVE:
+ // 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.
+ saveWebpageArchive(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();
+ }
+ break;
- // Show the banner ad in the free flavor.
- if (BuildConfig.FLAVOR.contentEquals("free")) {
- // Initialize the ads. If this isn't the first run, `loadAd()` will be automatically called instead.
- AdHelper.initializeAds(findViewById(R.id.adview), getApplicationContext(), getSupportFragmentManager(), getString(R.string.google_app_id), getString(R.string.ad_unit_id));
+ case StoragePermissionDialog.SAVE_IMAGE:
+ // 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, this, saveWebpageFilePath, currentWebView).execute();
+ } else { // The storage permission was not granted.
+ // Display an error snackbar.
+ Snackbar.make(currentWebView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show();
+ }
+ break;
}
- // Remove the `SYSTEM_UI` flags from the root frame layout.
- rootFrameLayout.setSystemUiVisibility(0);
+ // Reset the strings.
+ openFilePath = "";
+ saveWebpageUrl = "";
+ saveWebpageFilePath = "";
}
}
// Remove the lint warning below that the input method manager might be null.
assert inputMethodManager != null;
- // Initialize the foreground color spans for highlighting the URLs. We have to use the deprecated `getColor()` until API >= 23.
- redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700));
+ // Initialize the gray foreground color spans for highlighting the URLs. The deprecated `getResources()` must be used until API >= 23.
initialGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500));
finalGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500));
+ // Get the current theme status.
+ int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
+
+ // Set the red color span according to the theme.
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+ redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700));
+ } else {
+ redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_900));
+ }
+
// Get handles for the URL views.
EditText urlEditText = findViewById(R.id.url_edittext);
this.registerReceiver(orbotStatusBroadcastReceiver, new IntentFilter("org.torproject.android.intent.action.STATUS"));
// Get handles for views that need to be modified.
- DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
NavigationView navigationView = findViewById(R.id.navigationview);
- TabLayout tabLayout = findViewById(R.id.tablayout);
SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
- ViewPager webViewPager = findViewById(R.id.webviewpager);
ListView bookmarksListView = findViewById(R.id.bookmarks_drawer_listview);
FloatingActionButton launchBookmarksActivityFab = findViewById(R.id.launch_bookmarks_activity_fab);
FloatingActionButton createBookmarkFolderFab = findViewById(R.id.create_bookmark_folder_fab);
// 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 0 based.
+ // Get handles for the navigation menu and the back and forward menu items.
Menu navigationMenu = navigationView.getMenu();
- MenuItem navigationBackMenuItem = navigationMenu.getItem(2);
- MenuItem navigationForwardMenuItem = navigationMenu.getItem(3);
- MenuItem navigationHistoryMenuItem = navigationMenu.getItem(4);
- MenuItem navigationRequestsMenuItem = navigationMenu.getItem(6);
+ MenuItem navigationBackMenuItem = navigationMenu.findItem(R.id.back);
+ MenuItem navigationForwardMenuItem = navigationMenu.findItem(R.id.forward);
+ MenuItem navigationHistoryMenuItem = navigationMenu.findItem(R.id.history);
+ MenuItem navigationRequestsMenuItem = navigationMenu.findItem(R.id.requests);
// Update the web view pager every time a tab is modified.
webViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
// Set the current WebView.
setCurrentWebView(position);
- // Select the corresponding tab if it does not match the currently selected page. This will happen if the page was scrolled via swiping in the view pager or by creating a new tab.
+ // Select the corresponding tab if it does not match the currently selected page. This will happen if the page was scrolled by creating a new tab.
if (tabLayout.getSelectedTabPosition() != position) {
- // Create a handler to select the tab.
- Handler selectTabHandler = new Handler();
-
- // Create a runnable to select the tab.
- Runnable selectTabRunnable = () -> {
+ // Wait until the new tab has been created.
+ tabLayout.post(() -> {
// Get a handle for the tab.
TabLayout.Tab tab = tabLayout.getTabAt(position);
// Select the tab.
tab.select();
- };
-
- // Select the tab layout after 150 milliseconds, which leaves enough time for a new tab to be inflated.
- selectTabHandler.postDelayed(selectTabRunnable, 150);
+ });
}
}
defaultProgressViewStartOffset = swipeRefreshLayout.getProgressViewStartOffset();
defaultProgressViewEndOffset = swipeRefreshLayout.getProgressViewEndOffset();
- // Get the current theme status.
- int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
-
// Set the refresh color scheme according to the theme.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- swipeRefreshLayout.setColorSchemeResources(R.color.blue_500);
- } else {
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
swipeRefreshLayout.setColorSchemeResources(R.color.blue_700);
+ } else {
+ swipeRefreshLayout.setColorSchemeResources(R.color.violet_500);
}
// Initialize a color background typed value.
// Replace the header that `WebView` creates for `X-Requested-With` with a null value. The default value is the application ID (com.stoutner.privacybrowser.standard).
customHeaders.put("X-Requested-With", "");
- // Inflate a bare WebView to get the default user agent. It is not used to render content on the screen.
- @SuppressLint("InflateParams") View webViewLayout = getLayoutInflater().inflate(R.layout.bare_webview, null, false);
+ // Inflate a bare WebView to get the default user agent. It is not used to render content on the screen.
+ @SuppressLint("InflateParams") View webViewLayout = getLayoutInflater().inflate(R.layout.bare_webview, null, false);
+
+ // Get a handle for the WebView.
+ WebView bareWebView = webViewLayout.findViewById(R.id.bare_webview);
+
+ // Store the default user agent.
+ webViewDefaultUserAgent = bareWebView.getSettings().getUserAgentString();
+
+ // Destroy the bare WebView.
+ bareWebView.destroy();
+ }
+
+ private void applyAppSettings() {
+ // Get a handle for the shared preferences.
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+
+ // Store the values from the shared preferences in variables.
+ incognitoModeEnabled = sharedPreferences.getBoolean("incognito_mode", false);
+ boolean doNotTrackEnabled = sharedPreferences.getBoolean("do_not_track", false);
+ sanitizeGoogleAnalytics = sharedPreferences.getBoolean("google_analytics", true);
+ sanitizeFacebookClickIds = sharedPreferences.getBoolean("facebook_click_ids", true);
+ sanitizeTwitterAmpRedirects = sharedPreferences.getBoolean("twitter_amp_redirects", true);
+ 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);
+
+ // Apply the saved proxy mode if the app has been restarted.
+ if (savedProxyMode != null) {
+ // Apply the saved proxy mode.
+ proxyMode = savedProxyMode;
+
+ // Reset the saved proxy mode.
+ savedProxyMode = null;
+ }
+
+ // 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 a handle for the app compat delegate.
+ AppCompatDelegate appCompatDelegate = getDelegate();
+
+ // Get handles for the views that need to be modified.
+ FrameLayout rootFrameLayout = findViewById(R.id.root_framelayout);
+ ActionBar actionBar = appCompatDelegate.getSupportActionBar();
+
+ // Remove the incorrect lint warning below that the action bar might be null.
+ assert actionBar != null;
+
+ // Apply the proxy.
+ applyProxy(false);
+
+ // Set Do Not Track status.
+ if (doNotTrackEnabled) {
+ customHeaders.put("DNT", "1");
+ } else {
+ customHeaders.remove("DNT");
+ }
+
+ // Get the current layout parameters. Using coordinator layout parameters allows the `setBehavior()` command and using app bar layout parameters allows the `setScrollFlags()` command.
+ CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams();
+ AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) toolbar.getLayoutParams();
+ AppBarLayout.LayoutParams findOnPageLayoutParams = (AppBarLayout.LayoutParams) findOnPageLinearLayout.getLayoutParams();
+ AppBarLayout.LayoutParams tabsLayoutParams = (AppBarLayout.LayoutParams) tabsLinearLayout.getLayoutParams();
+
+ // Add the scrolling behavior to the layout parameters.
+ if (scrollAppBar) {
+ // Enable scrolling of the app bar.
+ swipeRefreshLayoutParams.setBehavior(new AppBarLayout.ScrollingViewBehavior());
+ toolbarLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP);
+ findOnPageLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP);
+ tabsLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP);
+ } else {
+ // Disable scrolling of the app bar.
+ swipeRefreshLayoutParams.setBehavior(null);
+ toolbarLayoutParams.setScrollFlags(0);
+ findOnPageLayoutParams.setScrollFlags(0);
+ tabsLayoutParams.setScrollFlags(0);
+
+ // Expand the app bar if it is currently collapsed.
+ appBarLayout.setExpanded(true);
+ }
+
+ // Apply the modified layout parameters.
+ swipeRefreshLayout.setLayoutParams(swipeRefreshLayoutParams);
+ toolbar.setLayoutParams(toolbarLayoutParams);
+ findOnPageLinearLayout.setLayoutParams(findOnPageLayoutParams);
+ tabsLinearLayout.setLayoutParams(tabsLayoutParams);
+
+ // Set the app bar scrolling for each WebView.
+ 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 modify 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);
+
+ // Set the app bar scrolling.
+ nestedScrollWebView.setNestedScrollingEnabled(scrollAppBar);
+ }
+ }
+
+ // Update the full screen browsing mode settings.
+ if (fullScreenBrowsingModeEnabled && inFullScreenBrowsingMode) { // Privacy Browser is currently in full screen browsing mode.
+ // Update the visibility of the app bar, which might have changed in the settings.
+ if (hideAppBar) {
+ // Hide the tab linear layout.
+ tabsLinearLayout.setVisibility(View.GONE);
+
+ // Hide the action bar.
+ actionBar.hide();
+ } else {
+ // Show the tab linear layout.
+ tabsLinearLayout.setVisibility(View.VISIBLE);
+
+ // Show the action bar.
+ actionBar.show();
+ }
+
+ // Hide the banner ad in the free flavor.
+ if (BuildConfig.FLAVOR.contentEquals("free")) {
+ AdHelper.hideAd(findViewById(R.id.adview));
+ }
+
+ /* 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 { // Privacy Browser is not in full screen browsing mode.
+ // Reset the full screen tracker, which could be true if Privacy Browser was in full screen mode before entering settings and full screen browsing was disabled.
+ inFullScreenBrowsingMode = false;
+
+ // Show the tab linear layout.
+ tabsLinearLayout.setVisibility(View.VISIBLE);
- // Get a handle for the WebView.
- WebView bareWebView = webViewLayout.findViewById(R.id.bare_webview);
+ // Show the action bar.
+ actionBar.show();
- // Store the default user agent.
- webViewDefaultUserAgent = bareWebView.getSettings().getUserAgentString();
+ // Show the banner ad in the free flavor.
+ if (BuildConfig.FLAVOR.contentEquals("free")) {
+ // Initialize the ads. If this isn't the first run, `loadAd()` will be automatically called instead.
+ AdHelper.initializeAds(findViewById(R.id.adview), getApplicationContext(), getSupportFragmentManager(), getString(R.string.google_app_id), getString(R.string.ad_unit_id));
+ }
- // Destroy the bare WebView.
- bareWebView.destroy();
+ // Remove the `SYSTEM_UI` flags from the root frame layout.
+ rootFrameLayout.setSystemUiVisibility(0);
+ }
}
@Override
public void navigateHistory(String url, int steps) {
// Apply the domain settings.
- applyDomainSettings(currentWebView, url, false, false);
+ applyDomainSettings(currentWebView, url, false, false, false);
// Load the history entry.
currentWebView.goBackOrForward(steps);
String previousUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() - 1).getUrl();
// Apply the domain settings.
- applyDomainSettings(currentWebView, previousUrl, false, false);
+ applyDomainSettings(currentWebView, previousUrl, false, false, false);
// Go back.
currentWebView.goBack();
// `reloadWebsite` is used if returning from the Domains activity. Otherwise JavaScript might not function correctly if it is newly enabled.
@SuppressLint("SetJavaScriptEnabled")
- private void applyDomainSettings(NestedScrollWebView nestedScrollWebView, String url, boolean resetTab, boolean reloadWebsite) {
+ private void applyDomainSettings(NestedScrollWebView nestedScrollWebView, String url, boolean resetTab, boolean reloadWebsite, boolean loadUrl) {
// Store the current URL.
nestedScrollWebView.setCurrentUrl(url);
// Get the current page position.
int currentPagePosition = webViewPagerAdapter.getPositionForId(nestedScrollWebView.getWebViewFragmentId());
- // Get a handle for the tab layout.
- TabLayout tabLayout = findViewById(R.id.tablayout);
-
// Get the corresponding tab.
TabLayout.Tab tab = tabLayout.getTabAt(currentPagePosition);
String defaultFontSizeString = sharedPreferences.getString("font_size", getString(R.string.font_size_default_value));
String defaultUserAgentName = sharedPreferences.getString("user_agent", getString(R.string.user_agent_default_value));
boolean defaultSwipeToRefresh = sharedPreferences.getBoolean("swipe_to_refresh", true);
+ String webViewTheme = sharedPreferences.getString("webview_theme", getString(R.string.webview_theme_default_value));
boolean wideViewport = sharedPreferences.getBoolean("wide_viewport", true);
boolean displayWebpageImages = sharedPreferences.getBoolean("display_webpage_images", true);
+ // Get the WebView theme entry values string array.
+ String[] webViewThemeEntryValuesStringArray = getResources().getStringArray(R.array.webview_theme_entry_values);
+
// Get a handle for the cookie manager.
CookieManager cookieManager = CookieManager.getInstance();
// Get the settings from the cursor.
nestedScrollWebView.setDomainSettingsDatabaseId(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper._ID)));
- nestedScrollWebView.setDomainSettingsJavaScriptEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_JAVASCRIPT)) == 1);
+ nestedScrollWebView.getSettings().setJavaScriptEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_JAVASCRIPT)) == 1);
nestedScrollWebView.setAcceptFirstPartyCookies(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FIRST_PARTY_COOKIES)) == 1);
boolean domainThirdPartyCookiesEnabled = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_THIRD_PARTY_COOKIES)) == 1);
nestedScrollWebView.getSettings().setDomStorageEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_DOM_STORAGE)) == 1);
String userAgentName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.USER_AGENT));
int fontSize = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.FONT_SIZE));
int swipeToRefreshInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SWIPE_TO_REFRESH));
- int nightModeInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.NIGHT_MODE));
+ int webViewThemeInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.WEBVIEW_THEME));
int wideViewportInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.WIDE_VIEWPORT));
int displayWebpageImagesInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.DISPLAY_IMAGES));
boolean pinnedSslCertificate = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE)) == 1);
boolean pinnedIpAddresses = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_IP_ADDRESSES)) == 1);
String pinnedHostIpAddresses = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.IP_ADDRESSES));
- // Create the pinned SSL date variables.
+ // Get the pinned SSL date longs.
+ long pinnedSslStartDateLong = currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE));
+ long pinnedSslEndDateLong = currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE));
+
+ // Define the pinned SSL date variables.
Date pinnedSslStartDate;
Date pinnedSslEndDate;
- // Set the pinned SSL certificate start date to `null` if the saved date `long` is 0 because creating a new Date results in an error if the input is 0.
- if (currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE)) == 0) {
+ // Set the pinned SSL certificate start date to `null` if the saved date long is 0 because creating a new date results in an error if the input is 0.
+ if (pinnedSslStartDateLong == 0) {
pinnedSslStartDate = null;
} else {
- pinnedSslStartDate = new Date(currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE)));
+ pinnedSslStartDate = new Date(pinnedSslStartDateLong);
}
- // Set the pinned SSL certificate end date to `null` if the saved date `long` is 0 because creating a new Date results in an error if the input is 0.
- if (currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE)) == 0) {
+ // Set the pinned SSL certificate end date to `null` if the saved date long is 0 because creating a new date results in an error if the input is 0.
+ if (pinnedSslEndDateLong == 0) {
pinnedSslEndDate = null;
} else {
- pinnedSslEndDate = new Date(currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE)));
+ pinnedSslEndDate = new Date(pinnedSslEndDateLong);
}
+ // Close the current host domain settings cursor.
+ currentDomainSettingsCursor.close();
+
// If there is a pinned SSL certificate, store it in the WebView.
if (pinnedSslCertificate) {
nestedScrollWebView.setPinnedSslCertificate(pinnedSslIssuedToCName, pinnedSslIssuedToOName, pinnedSslIssuedToUName, pinnedSslIssuedByCName, pinnedSslIssuedByOName, pinnedSslIssuedByUName,
nestedScrollWebView.setPinnedIpAddresses(pinnedHostIpAddresses);
}
- // Set night mode according to the night mode int.
- switch (nightModeInt) {
- case DomainsDatabaseHelper.SYSTEM_DEFAULT:
- // Set night mode according to the current default.
- nestedScrollWebView.setNightMode(sharedPreferences.getBoolean("night_mode", false));
- break;
-
- case DomainsDatabaseHelper.ENABLED:
- // Enable night mode.
- nestedScrollWebView.setNightMode(true);
- break;
-
- case DomainsDatabaseHelper.DISABLED:
- // Disable night mode.
- nestedScrollWebView.setNightMode(false);
- break;
- }
-
- // Enable JavaScript if night mode is enabled.
- if (nestedScrollWebView.getNightMode()) {
- // Enable JavaScript.
- nestedScrollWebView.getSettings().setJavaScriptEnabled(true);
- } else {
- // Set JavaScript according to the domain settings.
- nestedScrollWebView.getSettings().setJavaScriptEnabled(nestedScrollWebView.getDomainSettingsJavaScriptEnabled());
- }
-
- // Close the current host domain settings cursor.
- currentDomainSettingsCursor.close();
-
- // Apply the domain settings.
+ // Apply the cookie domain settings.
cookieManager.setAcceptCookie(nestedScrollWebView.getAcceptFirstPartyCookies());
// Set third-party cookies status if API >= 21.
swipeRefreshLayout.setEnabled(false);
}
+ // Check to see if WebView themes are supported.
+ if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
+ // Set the WebView theme.
+ switch (webViewThemeInt) {
+ case DomainsDatabaseHelper.SYSTEM_DEFAULT:
+ // Set the WebView theme. A switch statement cannot be used because the WebView theme entry values string array is not a compile time constant.
+ if (webViewTheme.equals(webViewThemeEntryValuesStringArray[1])) { // The light theme is selected.
+ // Turn off the WebView dark mode.
+ WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+ } else if (webViewTheme.equals(webViewThemeEntryValuesStringArray[2])) { // The dark theme is selected.
+ // Turn on the WebView dark mode.
+ WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
+ } else { // The system default theme is selected.
+ // Get the current system theme status.
+ int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
+
+ // Set the WebView theme according to the current system theme status.
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { // The system is in day mode.
+ // Turn off the WebView dark mode.
+ WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+ } else { // The system is in night mode.
+ // Turn on the WebView dark mode.
+ WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
+ }
+ }
+ break;
+
+ case DomainsDatabaseHelper.LIGHT_THEME:
+ // Turn off the WebView dark mode.
+ WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+ break;
+
+ case DomainsDatabaseHelper.DARK_THEME:
+ // Turn on the WebView dark mode.
+ WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
+ break;
+ }
+ }
+
// Set the viewport.
switch (wideViewportInt) {
case DomainsDatabaseHelper.SYSTEM_DEFAULT:
// Get the current theme status.
int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
- // Set a background on the URL relative layout to indicate that custom domain settings are being used. The deprecated `.getDrawable()` must be used until the minimum API >= 21.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- urlRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_dark_blue));
+ // Set a background on the URL relative layout to indicate that custom domain settings are being used.
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+ urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.drawable.url_bar_background_light_green, null));
} else {
- urlRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_light_green));
+ urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.drawable.url_bar_background_dark_blue, null));
}
} else { // The new URL does not have custom domain settings. Load the defaults.
// Store the values from the shared preferences.
- boolean defaultJavaScriptEnabled = sharedPreferences.getBoolean("javascript", false);
+ nestedScrollWebView.getSettings().setJavaScriptEnabled(sharedPreferences.getBoolean("javascript", false));
nestedScrollWebView.setAcceptFirstPartyCookies(sharedPreferences.getBoolean("first_party_cookies", false));
boolean defaultThirdPartyCookiesEnabled = sharedPreferences.getBoolean("third_party_cookies", false);
nestedScrollWebView.getSettings().setDomStorageEnabled(sharedPreferences.getBoolean("dom_storage", false));
nestedScrollWebView.enableBlocklist(NestedScrollWebView.ULTRALIST, sharedPreferences.getBoolean("ultralist", true));
nestedScrollWebView.enableBlocklist(NestedScrollWebView.ULTRAPRIVACY, sharedPreferences.getBoolean("ultraprivacy", true));
nestedScrollWebView.enableBlocklist(NestedScrollWebView.THIRD_PARTY_REQUESTS, sharedPreferences.getBoolean("block_all_third_party_requests", false));
- nestedScrollWebView.setNightMode(sharedPreferences.getBoolean("night_mode", false));
-
- // Enable JavaScript if night mode is enabled.
- if (nestedScrollWebView.getNightMode()) {
- // Enable JavaScript.
- nestedScrollWebView.getSettings().setJavaScriptEnabled(true);
- } else {
- // Set JavaScript according to the domain settings.
- nestedScrollWebView.getSettings().setJavaScriptEnabled(defaultJavaScriptEnabled);
- }
// Apply the default first-party cookie setting.
cookieManager.setAcceptCookie(nestedScrollWebView.getAcceptFirstPartyCookies());
nestedScrollWebView.getSettings().setUserAgentString(userAgentDataArray[userAgentArrayPosition]);
}
+ // Apply the WebView theme if supported by the installed WebView.
+ if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
+ // Set the WebView theme. A switch statement cannot be used because the WebView theme entry values string array is not a compile time constant.
+ if (webViewTheme.equals(webViewThemeEntryValuesStringArray[1])) { // The light theme is selected.
+ // Turn off the WebView dark mode.
+ WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+ } else if (webViewTheme.equals(webViewThemeEntryValuesStringArray[2])) { // The dark theme is selected.
+ // Turn on the WebView dark mode.
+ WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
+ } else { // The system default theme is selected.
+ // Get the current system theme status.
+ int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
+
+ // Set the WebView theme according to the current system theme status.
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { // The system is in day mode.
+ // Turn off the WebView dark mode.
+ WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+ } else { // The system is in night mode.
+ // Turn on the WebView dark mode.
+ WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
+ }
+ }
+ }
+
// Set the viewport.
nestedScrollWebView.getSettings().setUseWideViewPort(wideViewport);
// Set the loading of webpage images.
nestedScrollWebView.getSettings().setLoadsImagesAutomatically(displayWebpageImages);
- // Set a transparent background on URL edit text. The deprecated `getResources().getDrawable()` must be used until the minimum API >= 21.
- urlRelativeLayout.setBackground(getResources().getDrawable(R.color.transparent));
+ // Set a transparent background on URL edit text.
+ urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.color.transparent, null));
}
// Close the domains database helper.
if (reloadWebsite) {
nestedScrollWebView.reload();
}
+
+ // Load the URL if directed. This makes sure that the domain settings are properly loaded before the URL. By using `loadUrl()`, instead of `loadUrlFromBase()`, the Referer header will never be sent.
+ if (loadUrl) {
+ nestedScrollWebView.loadUrl(url, customHeaders);
+ }
}
private void applyProxy(boolean reloadWebViews) {
- // Get a handle for the app bar layout.
- AppBarLayout appBarLayout = findViewById(R.id.appbar_layout);
-
// 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);
case ProxyHelper.TOR:
// Set the app bar background to indicate proxying through Orbot is enabled.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- appBarLayout.setBackgroundResource(R.color.dark_blue_30);
- } else {
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
appBarLayout.setBackgroundResource(R.color.blue_50);
+ } else {
+ appBarLayout.setBackgroundResource(R.color.dark_blue_30);
}
// Check to see if Orbot is installed.
case ProxyHelper.I2P:
// Set the app bar background to indicate proxying through Orbot is enabled.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- appBarLayout.setBackgroundResource(R.color.dark_blue_30);
- } else {
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
appBarLayout.setBackgroundResource(R.color.blue_50);
+ } else {
+ appBarLayout.setBackgroundResource(R.color.dark_blue_30);
}
// Check to see if I2P is installed.
case ProxyHelper.CUSTOM:
// Set the app bar background to indicate proxying through Orbot is enabled.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- appBarLayout.setBackgroundResource(R.color.dark_blue_30);
- } else {
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
appBarLayout.setBackgroundResource(R.color.blue_50);
+ } else {
+ appBarLayout.setBackgroundResource(R.color.dark_blue_30);
}
break;
}
// Get handles for the menu items.
MenuItem privacyMenuItem = optionsMenu.findItem(R.id.toggle_javascript);
MenuItem firstPartyCookiesMenuItem = optionsMenu.findItem(R.id.toggle_first_party_cookies);
- MenuItem domStorageMenuItem = optionsMenu.findItem(R.id.toggle_dom_storage);
MenuItem refreshMenuItem = optionsMenu.findItem(R.id.refresh);
// Update the privacy icon.
if (currentWebView.getAcceptFirstPartyCookies()) { // First-party cookies are enabled.
firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_enabled);
} else { // First-party cookies are disabled.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_disabled_night);
- } else {
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_disabled_day);
+ } else {
+ firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_disabled_night);
}
}
- // Update the DOM storage icon.
- if (currentWebView.getSettings().getJavaScriptEnabled() && currentWebView.getSettings().getDomStorageEnabled()) { // Both JavaScript and DOM storage are enabled.
- domStorageMenuItem.setIcon(R.drawable.dom_storage_enabled);
- } else if (currentWebView.getSettings().getJavaScriptEnabled()) { // JavaScript is enabled but DOM storage is disabled.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- domStorageMenuItem.setIcon(R.drawable.dom_storage_disabled_night);
+ // Update the refresh icon.
+ if (refreshMenuItem.getTitle() == getString(R.string.refresh)) { // The refresh icon is displayed.
+ // Set the icon according to the theme.
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+ refreshMenuItem.setIcon(R.drawable.refresh_enabled_day);
} else {
- domStorageMenuItem.setIcon(R.drawable.dom_storage_disabled_day);
+ refreshMenuItem.setIcon(R.drawable.refresh_enabled_night);
}
- } else { // JavaScript is disabled, so DOM storage is ghosted.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- domStorageMenuItem.setIcon(R.drawable.dom_storage_ghosted_night);
+ } else { // The stop icon is displayed.
+ // Set the icon according to the theme.
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+ refreshMenuItem.setIcon(R.drawable.close_blue_day);
} else {
- domStorageMenuItem.setIcon(R.drawable.dom_storage_ghosted_day);
+ refreshMenuItem.setIcon(R.drawable.close_blue_night);
}
}
- // Update the refresh icon.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- refreshMenuItem.setIcon(R.drawable.refresh_enabled_night);
- } else {
- refreshMenuItem.setIcon(R.drawable.refresh_enabled_day);
- }
-
// `invalidateOptionsMenu()` calls `onPrepareOptionsMenu()` and redraws the icons in the app bar.
if (runInvalidateOptionsMenu) {
invalidateOptionsMenu();
ultraList = combinedBlocklists.get(4);
ultraPrivacy = combinedBlocklists.get(5);
- // Add the first tab.
- addNewTab("", true);
+ // Check to see if the activity has been restarted with a saved state.
+ if ((savedStateArrayList == null) || (savedStateArrayList.size() == 0)) { // The activity has not been restarted or it was restarted on start to force the night theme.
+ // Add the first tab.
+ addNewTab("", true);
+ } else { // The activity has been restarted.
+ // Restore each tab. Once the minimum API >= 24, a `forEach()` command can be used.
+ for (int i = 0; i < savedStateArrayList.size(); i++) {
+ // Add a new tab.
+ tabLayout.addTab(tabLayout.newTab());
+
+ // Get the new tab.
+ TabLayout.Tab newTab = tabLayout.getTabAt(i);
+
+ // Remove the lint warning below that the current tab might be null.
+ assert newTab != null;
+
+ // Set a custom view on the new tab.
+ newTab.setCustomView(R.layout.tab_custom_view);
+
+ // Add the new page.
+ webViewPagerAdapter.restorePage(savedStateArrayList.get(i), savedNestedScrollWebViewStateArrayList.get(i));
+ }
+
+ // Reset the saved state variables.
+ savedStateArrayList = null;
+ savedNestedScrollWebViewStateArrayList = null;
+
+ // Restore the selected tab position.
+ if (savedTabPosition == 0) { // The first tab is selected.
+ // Set the first page as the current WebView.
+ setCurrentWebView(0);
+ } else { // the first tab is not selected.
+ // Move to the selected tab.
+ webViewPager.setCurrentItem(savedTabPosition);
+ }
+
+ // Get the intent that started the app.
+ Intent intent = getIntent();
+
+ // Reset the intent. This prevents a duplicate tab from being created on restart.
+ setIntent(new Intent());
+
+ // Get the information from the intent.
+ String intentAction = intent.getAction();
+ Uri intentUriData = intent.getData();
+ String intentStringExtra = intent.getStringExtra(Intent.EXTRA_TEXT);
+
+ // Determine if this is a web search.
+ boolean isWebSearch = ((intentAction != null) && intentAction.equals(Intent.ACTION_WEB_SEARCH));
+
+ // Only process the URI if it contains data or it is a web search. If the user pressed the desktop icon after the app was already running the URI will be null.
+ if (intentUriData != null || intentStringExtra != null || isWebSearch) {
+ // Get the shared preferences.
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+
+ // Create a URL string.
+ String url;
+
+ // If the intent action is a web search, perform the search.
+ if (isWebSearch) { // The intent is a web search.
+ // Create an encoded URL string.
+ String encodedUrlString;
+
+ // Sanitize the search input and convert it to a search.
+ try {
+ encodedUrlString = URLEncoder.encode(intent.getStringExtra(SearchManager.QUERY), "UTF-8");
+ } catch (UnsupportedEncodingException exception) {
+ encodedUrlString = "";
+ }
+
+ // Add the base search URL.
+ url = searchURL + encodedUrlString;
+ } else if (intentUriData != null) { // The intent contains a URL formatted as a URI.
+ // Set the intent data as the URL.
+ url = intentUriData.toString();
+ } else { // The intent contains a string, which might be a URL.
+ // Set the intent string as the URL.
+ url = intentStringExtra;
+ }
+
+ // Add a new tab if specified in the preferences.
+ if (sharedPreferences.getBoolean("open_intents_in_new_tab", true)) { // Load the URL in a new tab.
+ // Set the loading new intent flag.
+ loadingNewIntent = true;
+
+ // Add a new tab.
+ addNewTab(url, true);
+ } else { // Load the URL in the current tab.
+ // Make it so.
+ loadUrl(currentWebView, url);
+ }
+ }
+ }
}
public void addTab(View view) {
}
private void addNewTab(String url, boolean moveToTab) {
- // Sanitize the URL.
- url = sanitizeUrl(url);
-
- // Get a handle for the tab layout and the view pager.
- TabLayout tabLayout = findViewById(R.id.tablayout);
- ViewPager webViewPager = findViewById(R.id.webviewpager);
-
// Get the new page number. The page numbers are 0 indexed, so the new page number will match the current count.
int newTabNumber = tabLayout.getTabCount();
}
public void closeTab(View view) {
- // Get a handle for the tab layout.
- TabLayout tabLayout = findViewById(R.id.tablayout);
-
// Run the command according to the number of tabs.
if (tabLayout.getTabCount() > 1) { // There is more than one tab open.
// Close the current tab.
}
private void closeCurrentTab() {
- // Get handles for the views.
- AppBarLayout appBarLayout = findViewById(R.id.appbar_layout);
- TabLayout tabLayout = findViewById(R.id.tablayout);
- ViewPager webViewPager = findViewById(R.id.webviewpager);
+ // Pause the current WebView. This prevents buffered audio from playing after the tab is closed.
+ currentWebView.onPause();
// Get the current tab number.
int currentTabNumber = tabLayout.getSelectedTabPosition();
appBarLayout.setExpanded(true);
}
+ private void saveWebpageArchive(String filePath) {
+ // Save the webpage archive.
+ currentWebView.saveWebArchive(filePath);
+
+ // Display a snackbar.
+ Snackbar saveWebpageArchiveSnackbar = Snackbar.make(currentWebView, getString(R.string.file_saved) + " " + filePath, Snackbar.LENGTH_SHORT);
+
+ // Add an open option to the snackbar.
+ saveWebpageArchiveSnackbar.setAction(R.string.open, (View view) -> {
+ // Get a file for the file name string.
+ File file = new File(filePath);
+
+ // Declare a file URI variable.
+ Uri fileUri;
+
+ // Get the URI for the file according to the Android version.
+ if (Build.VERSION.SDK_INT >= 24) { // Use a file provider.
+ fileUri = FileProvider.getUriForFile(this, getString(R.string.file_provider), file);
+ } else { // Get the raw file path URI.
+ fileUri = Uri.fromFile(file);
+ }
+
+ // Get a handle for the content resolver.
+ ContentResolver contentResolver = getContentResolver();
+
+ // Create an open intent with `ACTION_VIEW`.
+ Intent openIntent = new Intent(Intent.ACTION_VIEW);
+
+ // Set the URI and the MIME type.
+ openIntent.setDataAndType(fileUri, contentResolver.getType(fileUri));
+
+ // Allow the app to read the file URI.
+ openIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+ // Show the chooser.
+ startActivity(Intent.createChooser(openIntent, getString(R.string.open)));
+ });
+
+ // Show the snackbar.
+ saveWebpageArchiveSnackbar.show();
+ }
+
private void clearAndExit() {
// Get a handle for the shared preferences.
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
}
}
+ // Clear the logcat.
+ if (clearEverything || sharedPreferences.getBoolean(getString(R.string.clear_logcat_key), true)) {
+ try {
+ // Clear the logcat. `-c` clears the logcat. `-b all` clears all the buffers (instead of just crash, main, and system).
+ Process process = Runtime.getRuntime().exec("logcat -b all -c");
+
+ // Wait for the process to finish.
+ process.waitFor();
+ } catch (IOException|InterruptedException exception) {
+ // Do nothing.
+ }
+ }
+
// Clear the cache.
if (clearEverything || sharedPreferences.getBoolean("clear_cache", true)) {
// Clear the cache from each WebView.
// Clear the back/forward history for this WebView.
nestedScrollWebView.clearHistory();
- // Destroy the internal state of `mainWebView`.
+ // Destroy the internal state of the WebView.
nestedScrollWebView.destroy();
}
}
System.exit(0);
}
+ public void bookmarksBack(View view) {
+ 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);
+
+ // Load the new folder.
+ loadBookmarksFolder();
+ }
+ }
+
private void setCurrentWebView(int pageNumber) {
// Get handles for the URL views.
RelativeLayout urlRelativeLayout = findViewById(R.id.url_relativelayout);
// Get the current theme status.
int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
- // Set a green background on the URL relative layout to indicate that custom domain settings are being used. The deprecated `.getDrawable()` must be used until the minimum API >= 21.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- urlRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_dark_blue));
+ // Set a green background on the URL relative layout to indicate that custom domain settings are being used.
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+ urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.drawable.url_bar_background_light_green, null));
} else {
- urlRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_light_green));
+ urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.drawable.url_bar_background_dark_blue, null));
}
} else {
- urlRelativeLayout.setBackground(getResources().getDrawable(R.color.transparent));
+ urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.color.transparent, null));
}
} else { // The fragment has not been populated. Try again in 100 milliseconds.
// Create a handler to set the current WebView.
}
@Override
- public void initializeWebView(NestedScrollWebView nestedScrollWebView, int pageNumber, ProgressBar progressBar, String url) {
+ public void initializeWebView(NestedScrollWebView nestedScrollWebView, int pageNumber, ProgressBar progressBar, String url, Boolean restoringState) {
+ // Get a handle for the shared preferences.
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+
+ // Get the WebView theme.
+ String webViewTheme = sharedPreferences.getString("webview_theme", getString(R.string.webview_theme_default_value));
+
+ // Get the WebView theme entry values string array.
+ String[] webViewThemeEntryValuesStringArray = getResources().getStringArray(R.array.webview_theme_entry_values);
+
+ // Apply the WebView theme if supported by the installed WebView.
+ if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
+ // Set the WebView theme. A switch statement cannot be used because the WebView theme entry values string array is not a compile time constant.
+ if (webViewTheme.equals(webViewThemeEntryValuesStringArray[1])) { // The light theme is selected.
+ // Turn off the WebView dark mode.
+ WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+
+ // Make the WebView visible. The WebView was created invisible in `webview_framelayout` to prevent a white background splash in night mode.
+ // If the system is currently in night mode, showing the WebView will be handled in `onProgressChanged()`.
+ nestedScrollWebView.setVisibility(View.VISIBLE);
+ } else if (webViewTheme.equals(webViewThemeEntryValuesStringArray[2])) { // The dark theme is selected.
+ // Turn on the WebView dark mode.
+ WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
+ } else { // The system default theme is selected.
+ // Get the current system theme status.
+ int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
+
+ // Set the WebView theme according to the current system theme status.
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { // The system is in day mode.
+ // Turn off the WebView dark mode.
+ WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+
+ // Make the WebView visible. The WebView was created invisible in `webview_framelayout` to prevent a white background splash in night mode.
+ // If the system is currently in night mode, showing the WebView will be handled in `onProgressChanged()`.
+ nestedScrollWebView.setVisibility(View.VISIBLE);
+ } else { // The system is in night mode.
+ // Turn on the WebView dark mode.
+ WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
+ }
+ }
+ }
+
// Get a handle for the app compat delegate.
AppCompatDelegate appCompatDelegate = getDelegate();
// Get handles for the activity views.
FrameLayout rootFrameLayout = findViewById(R.id.root_framelayout);
- DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
RelativeLayout mainContentRelativeLayout = findViewById(R.id.main_content_relativelayout);
ActionBar actionBar = appCompatDelegate.getSupportActionBar();
LinearLayout tabsLinearLayout = findViewById(R.id.tabs_linearlayout);
EditText urlEditText = findViewById(R.id.url_edittext);
- TabLayout tabLayout = findViewById(R.id.tablayout);
SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
// Remove the incorrect lint warning below that the action bar might be null.
// Remove the lint warning below that the input method manager might be null.
assert inputMethodManager != null;
- // Get a handle for the shared preferences.
- SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
-
// Initialize the favorite icon.
nestedScrollWebView.initializeFavoriteIcon();
// Hide the action bar.
actionBar.hide();
- // If the app bar is not being scrolled, the swipe refresh layout needs to be adjusted.
- if (!scrollAppBar) {
+ // Check to see if the app bar is normally scrolled.
+ if (scrollAppBar) { // The app bar is scrolled when it is displayed.
+ // Get the swipe refresh layout parameters.
+ CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams();
+
+ // Remove the off-screen scrolling layout.
+ swipeRefreshLayoutParams.setBehavior(null);
+ } else { // The app bar is not scrolled when it is displayed.
// Remove the padding from the top of the swipe refresh layout.
swipeRefreshLayout.setPadding(0, 0, 0, 0);
// Show the action bar.
actionBar.show();
- // If the app bar is not being scrolled, the swipe refresh layout needs to be adjusted.
- if (!scrollAppBar) {
+ // Check to see if the app bar is normally scrolled.
+ if (scrollAppBar) { // The app bar is scrolled when it is displayed.
+ // Get the swipe refresh layout parameters.
+ CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams();
+
+ // Add the off-screen scrolling layout.
+ swipeRefreshLayoutParams.setBehavior(new AppBarLayout.ScrollingViewBehavior());
+ } else { // The app bar is not scrolled when it is displayed.
// The swipe refresh layout must be manually moved below the app bar layout.
swipeRefreshLayout.setPadding(0, appBarHeight, 0, 0);
}
// Get the file name from the content disposition.
- String fileNameString = PrepareSaveDialog.getFileNameFromContentDisposition(this, contentDisposition, downloadUrl);
+ String fileNameString = PrepareSaveDialog.getFileNameFromHeaders(this, contentDisposition, mimetype, downloadUrl);
// Instantiate the save dialog.
- DialogFragment saveDialogFragment = SaveDialog.saveUrl(StoragePermissionDialog.SAVE_URL, downloadUrl, formattedFileSizeString, fileNameString, userAgent,
+ DialogFragment saveDialogFragment = SaveWebpageDialog.saveWebpage(StoragePermissionDialog.SAVE_URL, downloadUrl, formattedFileSizeString, fileNameString, userAgent,
nestedScrollWebView.getAcceptFirstPartyCookies());
// Show the save dialog. It must be named `save_dialog` so that the file picker can update the file name.
});
}
- // TODO.
- if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
- WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_AUTO);
- }
-
// Set the web chrome client.
nestedScrollWebView.setWebChromeClient(new WebChromeClient() {
// Update the progress bar when a page is loading.
@Override
public void onProgressChanged(WebView view, int progress) {
- // Inject the night mode CSS if night mode is enabled.
- if (nestedScrollWebView.getNightMode()) { // Night mode is enabled.
- // `background-color: #212121` sets the background to be dark gray. `color: #BDBDBD` sets the text color to be light gray. `box-shadow: none` removes a lower underline on links
- // used by WordPress. `text-decoration: none` removes all text underlines. `text-shadow: none` removes text shadows, which usually have a hard coded color.
- // `border: none` removes all borders, which can also be used to underline text. `a {color: #1565C0}` sets links to be a dark blue.
- // `::selection {background: #0D47A1}' sets the text selection highlight color to be a dark blue. `!important` takes precedent over any existing sub-settings.
- nestedScrollWebView.evaluateJavascript("(function() {var parent = document.getElementsByTagName('head').item(0); var style = document.createElement('style'); style.type = 'text/css'; " +
- "style.innerHTML = '* {background-color: #212121 !important; color: #BDBDBD !important; box-shadow: none !important; text-decoration: none !important;" +
- "text-shadow: none !important; border: none !important;} a {color: #1565C0 !important;} ::selection {background: #0D47A1 !important;}'; parent.appendChild(style)})()", value -> {
- // Initialize a handler to display `mainWebView`.
- Handler displayWebViewHandler = new Handler();
-
- // Setup a runnable to display `mainWebView` after a delay to allow the CSS to be applied.
- Runnable displayWebViewRunnable = () -> {
- // Only display `mainWebView` if the progress bar is gone. This prevents the display of the `WebView` while it is still loading.
- if (progressBar.getVisibility() == View.GONE) {
- nestedScrollWebView.setVisibility(View.VISIBLE);
- }
- };
-
- // Display the WebView after 500 milliseconds.
- displayWebViewHandler.postDelayed(displayWebViewRunnable, 500);
- });
- } else { // Night mode is disabled.
- // Display the nested scroll WebView if night mode is disabled.
- // Because of a race condition between `applyDomainSettings` and `onPageStarted`,
- // when night mode is set by domain settings the WebView may be hidden even if night mode is not currently enabled.
- nestedScrollWebView.setVisibility(View.VISIBLE);
- }
-
// Update the progress bar.
progressBar.setProgress(progress);
//Stop the swipe to refresh indicator if it is running
swipeRefreshLayout.setRefreshing(false);
+
+ // Make the current WebView visible. If this is a new tab, the current WebView would have been created invisible in `webview_framelayout` to prevent a white background splash in night mode.
+ nestedScrollWebView.setVisibility(View.VISIBLE);
}
}
// 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;
+ // Only populate the title text view if the tab view has been fully populated.
+ if (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 according to the URL.
- if (title.equals("about:blank")) {
- // Set the title to indicate a new tab.
- tabTitleTextView.setText(R.string.new_tab);
- } else {
- // Set the title as the tab text.
- tabTitleTextView.setText(title);
+ // Set the title according to the URL.
+ if (title.equals("about:blank")) {
+ // Set the title to indicate a new tab.
+ tabTitleTextView.setText(R.string.new_tab);
+ } else {
+ // Set the title as the tab text.
+ tabTitleTextView.setText(title);
+ }
}
}
}
// Handle the URL according to the type.
if (url.startsWith("http")) { // Load the URL in Privacy Browser.
- // Apply the domain settings for the new URL. This doesn't do anything if the domain has not changed.
- applyDomainSettings(nestedScrollWebView, url, true, false);
-
- // Manually load the URL. The changing of the user agent will cause WebView to reload the previous URL.
- nestedScrollWebView.loadUrl(url, customHeaders);
+ // Load the URL. By using `loadUrl()`, instead of `loadUrlFromBase()`, the Referer header will never be sent.
+ loadUrl(nestedScrollWebView, url);
// Returning true indicates that Privacy Browser is manually handling the loading of the URL.
// Custom headers cannot be added if false is returned and the WebView handles the loading of the URL.
// Open the email program in a new task instead of as part of Privacy Browser.
emailIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- // Make it so.
- startActivity(emailIntent);
+ try {
+ // Make it so.
+ startActivity(emailIntent);
+ } catch (ActivityNotFoundException exception) {
+ // Display a snackbar.
+ Snackbar.make(currentWebView, getString(R.string.error) + " " + exception, Snackbar.LENGTH_INDEFINITE).show();
+ }
+
// Returning true indicates Privacy Browser is handling the URL by creating an intent.
return true;
// Open the dialer in a new task instead of as part of Privacy Browser.
dialIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- // Make it so.
- startActivity(dialIntent);
+ try {
+ // Make it so.
+ startActivity(dialIntent);
+ } catch (ActivityNotFoundException exception) {
+ // Display a snackbar.
+ Snackbar.make(currentWebView, getString(R.string.error) + " " + exception, Snackbar.LENGTH_INDEFINITE).show();
+ }
// Returning true indicates Privacy Browser is handling the URL by creating an intent.
return true;
// Get a handle for the navigation menu.
Menu navigationMenu = navigationView.getMenu();
- // Get a handle for the navigation requests menu item. The menu is 0 based.
- MenuItem navigationRequestsMenuItem = navigationMenu.getItem(6);
+ // Get a handle for the navigation requests menu item.
+ MenuItem navigationRequestsMenuItem = navigationMenu.findItem(R.id.requests);
// 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()));
// Get the preferences.
boolean scrollAppBar = sharedPreferences.getBoolean("scroll_app_bar", true);
- // Get a handler for the app bar layout.
- AppBarLayout appBarLayout = findViewById(R.id.appbar_layout);
-
// Set the top padding of the swipe refresh layout according to the app bar scrolling preference. This can't be done in `appAppSettings()` because the app bar is not yet populated there.
if (scrollAppBar || (inFullScreenBrowsingMode && hideAppBar)) {
// No padding is needed because it will automatically be placed below the app bar layout due to the scrolling layout behavior.
// Reset the requests counters.
nestedScrollWebView.resetRequestsCounters();
- // TODO. Make the background of a new tab match the theme.
- // If night mode is enabled, hide `mainWebView` until after the night mode CSS is applied.
- if (nestedScrollWebView.getNightMode()) {
- nestedScrollWebView.setVisibility(View.INVISIBLE);
- } else {
- nestedScrollWebView.setVisibility(View.VISIBLE);
- }
-
- // Hide the keyboard.
- inputMethodManager.hideSoftInputFromWindow(nestedScrollWebView.getWindowToken(), 0);
-
// 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 and the URL edit text is not currently being edited.
+ if ((tabLayout.getSelectedTabPosition() == currentPagePosition) && !urlEditText.hasFocus()) {
// Display the formatted URL text.
urlEditText.setText(url);
// Apply text highlighting to `urlTextBox`.
highlightUrlText();
+
+ // Hide the keyboard.
+ inputMethodManager.hideSoftInputFromWindow(nestedScrollWebView.getWindowToken(), 0);
}
// Reset the list of host IP addresses.
refreshMenuItem.setTitle(R.string.stop);
// Get the app bar and theme preferences.
- boolean displayAdditionalAppBarIcons = sharedPreferences.getBoolean("display_additional_app_bar_icons", false);
+ boolean displayAdditionalAppBarIcons = sharedPreferences.getBoolean(getString(R.string.display_additional_app_bar_icons_key), false);
// If the icon is displayed in the AppBar, set it according to the theme.
if (displayAdditionalAppBarIcons) {
int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
// Set the stop icon according to the theme.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- refreshMenuItem.setIcon(R.drawable.close_night);
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+ refreshMenuItem.setIcon(R.drawable.close_blue_day);
} else {
- refreshMenuItem.setIcon(R.drawable.close_day);
+ refreshMenuItem.setIcon(R.drawable.close_blue_night);
}
}
}
refreshMenuItem.setTitle(R.string.refresh);
// Get the app bar and theme preferences.
- boolean displayAdditionalAppBarIcons = sharedPreferences.getBoolean("display_additional_app_bar_icons", false);
+ boolean displayAdditionalAppBarIcons = sharedPreferences.getBoolean(getString(R.string.display_additional_app_bar_icons_key), false);
// If the icon is displayed in the app bar, reset it according to the theme.
if (displayAdditionalAppBarIcons) {
int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
// Set the icon according to the theme.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- refreshMenuItem.setIcon(R.drawable.refresh_enabled_night);
- } else {
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
refreshMenuItem.setIcon(R.drawable.refresh_enabled_day);
+ } else {
+ refreshMenuItem.setIcon(R.drawable.refresh_enabled_night);
}
}
}
- // Clear the cache and history if Incognito Mode is enabled.
+ // Clear the cache, history, and logcat if Incognito Mode is enabled.
if (incognitoModeEnabled) {
// Clear the cache. `true` includes disk files.
nestedScrollWebView.clearCache(true);
// Delete the secondary `Service Worker` cache directory.
// A `String[]` must be used because the directory contains a space and `Runtime.exec` will not escape the string correctly otherwise.
Runtime.getRuntime().exec(new String[]{"rm", "-rf", privateDataDirectoryString + "/app_webview/Service Worker/"});
- } catch (IOException e) {
+ } catch (IOException exception) {
// Do nothing if an error is thrown.
}
+
+ // Clear the logcat.
+ try {
+ // Clear the logcat. `-c` clears the logcat. `-b all` clears all the buffers (instead of just crash, main, and system).
+ Runtime.getRuntime().exec("logcat -b all -c");
+ } catch (IOException exception) {
+ // Do nothing.
+ }
}
// 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);
- }
-
// 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();
inputMethodManager.showSoftInput(urlEditText, 0);
// Apply the domain settings. This clears any settings from the previous domain.
- applyDomainSettings(nestedScrollWebView, "", true, false);
+ applyDomainSettings(nestedScrollWebView, "", true, false, false);
// Only populate the title text view if the tab has been fully created.
if (tab != null) {
}
});
- // Check to see if this is the first page.
- if (pageNumber == 0) {
+ // Check to see if the state is being restored.
+ if (restoringState) { // The state is being restored.
+ // Resume the nested scroll WebView JavaScript timers.
+ nestedScrollWebView.resumeTimers();
+ } else if (pageNumber == 0) { // The first page is being loaded.
// Set this nested scroll WebView as the current WebView.
currentWebView = nestedScrollWebView;
- // Apply the app settings from the shared preferences.
- applyAppSettings();
-
// Initialize the URL to load string.
String urlToLoadString;
// Get the intent that started the app.
Intent launchingIntent = getIntent();
+ // Reset the intent. This prevents a duplicate tab from being created on restart.
+ setIntent(new Intent());
+
// Get the information from the intent.
String launchingIntentAction = launchingIntent.getAction();
Uri launchingIntentUriData = launchingIntent.getData();
+ String launchingIntentStringExtra = launchingIntent.getStringExtra(Intent.EXTRA_TEXT);
// Parse the launching intent URL.
if ((launchingIntentAction != null) && launchingIntentAction.equals(Intent.ACTION_WEB_SEARCH)) { // The intent contains a search string.
// Store the web search as the URL to load.
urlToLoadString = searchURL + encodedUrlString;
- } else if (launchingIntentUriData != null){ // The intent contains a URL.
- // Store the URL.
+ } else if (launchingIntentUriData != null) { // The launching intent contains a URL formatted as a URI.
+ // Store the URI as a URL.
urlToLoadString = launchingIntentUriData.toString();
+ } else if (launchingIntentStringExtra != null) { // The launching intent contains text that might be a URL.
+ // Store the URL.
+ urlToLoadString = launchingIntentStringExtra;
+ } else if (!url.equals("")) { // The activity has been restarted.
+ // Load the saved URL.
+ urlToLoadString = url;
} else { // The is no URL in the intent.
// Store the homepage to be loaded.
urlToLoadString = sharedPreferences.getString("homepage", getString(R.string.homepage_default_value));
} else { // Load the URL.
loadUrl(nestedScrollWebView, urlToLoadString);
}
- } else { // This is not the first tab.
- // Apply the domain settings.
- applyDomainSettings(nestedScrollWebView, url, false, false);
+ // Reset the intent. This prevents a duplicate tab from being created on a subsequent restart if loading an link from a new intent on restart.
+ // For example, this prevents a duplicate tab if a link is loaded from the Guide after changing the theme in the guide and then changing the theme again in the main activity.
+ setIntent(new Intent());
+ } else { // This is not the first tab.
// Load the URL.
- nestedScrollWebView.loadUrl(url, customHeaders);
+ loadUrl(nestedScrollWebView, url);
// Set the focus and display the keyboard if the URL is blank.
if (url.equals("")) {