import java.util.Map;
import java.util.Set;
-// TODO. The swipe refresh indicator needs to be enabled/disabled when switching tabs.
+// TODO. Store up reloads for tabs that are not visible.
+// TODO. New tabs are white in dark mode.
// AppCompatActivity from android.support.v7.app.AppCompatActivity must be used to have access to the SupportActionBar until the minimum API is >= 21.
public class MainWebViewActivity extends AppCompatActivity implements CreateBookmarkDialog.CreateBookmarkListener, CreateBookmarkFolderDialog.CreateBookmarkFolderListener,
// `allowScreenshots` is public static so it can be accessed from everywhere. It is also used in `onCreate()`.
public static boolean allowScreenshots;
- // TODO Remove.
- // `formattedUrlString` is public static so it can be accessed from `AddDomainDialog`, `BookmarksActivity`, `DomainSettingsFragment`, and `PinnedMismatchDialog`.
- // It is also used in `onCreate()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onCreateHomeScreenShortcutCreate()`, `loadUrlFromTextBox()`, and `applyProxyThroughOrbot()`.
- public static String formattedUrlString;
-
// `orbotStatus` is public static so it can be accessed from `OrbotProxyHelper`. It is also used in `onCreate()`, `onResume()`, and `applyProxyThroughOrbot()`.
public static String orbotStatus;
// The WebView pager adapter is accessed from `PinnedMismatchDialog`. It is also used in `onCreate()`, `onResume()`, and `addTab()`.
public static WebViewPagerAdapter webViewPagerAdapter;
- // `reloadOnRestart` is public static so it can be accessed from `SettingsFragment`. It is also used in `onRestart()`
+ // `reloadOnRestart` is public static so it can be accessed from `SettingsFragment`. It is used in `onRestart()`
public static boolean reloadOnRestart;
- // `reloadUrlOnRestart` is public static so it can be accessed from `SettingsFragment` and `BookmarksActivity`. It is also used in `onRestart()`.
+ // 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;
// `firstPartyCookiesEnabled` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, `onDownloadImage()`, `onDownloadFile()`, and `applyDomainSettings()`.
private boolean firstPartyCookiesEnabled;
- // `saveFormDataEnabled` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyDomainSettings()`. It can be removed once the minimum API >= 26.
- private boolean saveFormDataEnabled;
-
- // TODO Move to NestedScrollWebView.
- // `nightMode` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyDomainSettings()`.
- private boolean nightMode;
-
// 'homepage' is used in `onCreate()`, `onNavigationItemSelected()`, and `applyProxyThroughOrbot()`.
private String homepage; // TODO ?
// `searchURL` is used in `loadURLFromTextBox()` and `applyProxyThroughOrbot()`.
private String searchURL; // TODO ?
- // The options menu is set in `onCreateOptionsMenu()` and used in `onOptionsItemSelected()` and `updatePrivacyIcons()`.
+ // The options menu is set in `onCreateOptionsMenu()` and used in `onOptionsItemSelected()`, `updatePrivacyIcons()`, and `initializeWebView()`.
private Menu optionsMenu;
- // The refresh menu item is set in `onCreateOptionsMenu()` and accessed from `initializeWebView()`.
- // It must be this way because `initializeWebView()` runs before the menu is created but doesn't actually modify the menu until later.
- private MenuItem refreshMenuItem; // TODO. Create it from `optionsMenu`.
-
// TODO. This could probably be removed.
// The blocklist helper is used in `onCreate()` and `WebViewPagerAdapter`.
private BlockListHelper blockListHelper;
// `privateDataDirectoryString` is used in `onCreate()`, `onOptionsItemSelected()`, and `onNavigationItemSelected()`.
private String privateDataDirectoryString; // TODO.
- // `findOnPageEditText` is used in `onCreate()`, `onOptionsItemSelected()`, and `closeFindOnPage()`.
- private EditText findOnPageEditText; // TODO.
-
- // `displayAdditionalAppBarIcons` is used in `onCreate()` and `onCreateOptionsMenu()`.
- private boolean displayAdditionalAppBarIcons; // TODO.
-
// The action bar drawer toggle is initialized in `onCreate()` and used in `onResume()`.
- private ActionBarDrawerToggle actionBarDrawerToggle; // TODO.
+ private ActionBarDrawerToggle actionBarDrawerToggle;
// The color spans are used in `onCreate()` and `highlightUrlText()`.
private ForegroundColorSpan redColorSpan;
// `httpAuthHandler` is used in `onCreate()`, `onHttpAuthenticationCancel()`, and `onHttpAuthenticationProceed()`.
private static HttpAuthHandler httpAuthHandler; // TODO.
- // `inputMethodManager` is used in `onOptionsItemSelected()`, `loadUrlFromTextBox()`, and `closeFindOnPage()`.
- private InputMethodManager inputMethodManager; // TODO.
-
// `bookmarksDatabaseHelper` is used in `onCreate()`, `onDestroy`, `onOptionsItemSelected()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`,
// and `loadBookmarksFolder()`.
- private BookmarksDatabaseHelper bookmarksDatabaseHelper; // TODO.
-
- // `bookmarksListView` is used in `onCreate()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, and `loadBookmarksFolder()`.
- private ListView bookmarksListView; // TODO.
-
- // `bookmarksTitleTextView` is used in `onCreate()` and `loadBookmarksFolder()`.
- private TextView bookmarksTitleTextView; // TODO.
+ private BookmarksDatabaseHelper bookmarksDatabaseHelper;
// `bookmarksCursor` is used in `onDestroy()`, `onOptionsItemSelected()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and `loadBookmarksFolder()`.
private Cursor bookmarksCursor;
// `downloadImageUrl` is used in `onCreateContextMenu()` and `onRequestPermissionResult()`.
private String downloadImageUrl;
- // The user agent variables are used in `onCreate()` and `applyDomainSettings()`.
- private ArrayAdapter<CharSequence> userAgentNamesArray;
- private String[] userAgentDataArray;
-
// The request codes are used in `onCreate()`, `onCreateContextMenu()`, `onCloseDownloadLocationPermissionDialog()`, `onRequestPermissionResult()`, and `initializeWebView()`.
private final int DOWNLOAD_FILE_REQUEST_CODE = 1;
private final int DOWNLOAD_IMAGE_REQUEST_CODE = 2;
// Set the content view.
setContentView(R.layout.main_framelayout);
- // Get handles for the input method manager and toolbar.
- inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ // Get a handle for the input method.
+ InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+
+ // Remove the lint warning below that the input method manager might be null.
+ assert inputMethodManager != null;
+
+ // Get a handle for the toolbar.
Toolbar toolbar = findViewById(R.id.toolbar);
// Set the action bar. `SupportActionBar` must be used until the minimum API is >= 21.
setSupportActionBar(toolbar);
+
+ // Get a handle for the action bar.
ActionBar actionBar = getSupportActionBar();
// This is needed to get rid of the Android Studio warning that the action bar might be null.
// If Privacy Browser is waiting on Orbot, load the website now that Orbot is connected.
if (orbotStatus.equals("ON") && waitingForOrbot) {
- // Reset `waitingForOrbot`.
+ // Reset the waiting for Orbot status.
waitingForOrbot = false;
- // Load `formattedUrlString
- loadUrl(formattedUrlString);
+ // Get the intent that started the app.
+ Intent launchingIntent = getIntent();
+
+ // Get the information from the intent.
+ String launchingIntentAction = launchingIntent.getAction();
+ Uri launchingIntentUriData = launchingIntent.getData();
+
+ // If the intent action is a web search, perform the search.
+ if ((launchingIntentAction != null) && launchingIntentAction.equals(Intent.ACTION_WEB_SEARCH)) {
+ // Create an encoded URL string.
+ String encodedUrlString;
+
+ // Sanitize the search input and convert it to a search.
+ try {
+ encodedUrlString = URLEncoder.encode(launchingIntent.getStringExtra(SearchManager.QUERY), "UTF-8");
+ } catch (UnsupportedEncodingException exception) {
+ encodedUrlString = "";
+ }
+
+ // Load the completed search URL.
+ loadUrl(searchURL + encodedUrlString);
+ } else if (launchingIntentUriData != null){ // Check to see if the intent contains a new URL.
+ // Load the URL from the intent.
+ loadUrl(launchingIntentUriData.toString());
+ } else { // The is no URL in the intent.
+ // Load the homepage.
+ loadUrl(homepage);
+ }
}
}
};
TabLayout tabLayout = findViewById(R.id.tablayout);
SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
ViewPager webViewPager = findViewById(R.id.webviewpager);
- bookmarksListView = findViewById(R.id.bookmarks_drawer_listview);
- bookmarksTitleTextView = findViewById(R.id.bookmarks_title_textview);
+ 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);
FloatingActionButton createBookmarkFab = findViewById(R.id.create_bookmark_fab);
- findOnPageEditText = findViewById(R.id.find_on_page_edittext);
+ EditText findOnPageEditText = findViewById(R.id.find_on_page_edittext);
// Listen for touches on the navigation menu.
navigationView.setNavigationItemSelectedListener(this);
@Override
public void onTabReselected(TabLayout.Tab tab) {
// Instantiate the View SSL Certificate dialog.
- DialogFragment viewSslCertificateDialogFragment = ViewSslCertificateDialog.displayDialog(currentWebView.getWebViewFragmentId(), currentWebView.getFavoriteOrDefaultIcon());
+ DialogFragment viewSslCertificateDialogFragment = ViewSslCertificateDialog.displayDialog(currentWebView.getWebViewFragmentId());
// Display the View SSL Certificate dialog.
viewSslCertificateDialogFragment.show(getSupportFragmentManager(), getString(R.string.view_ssl_certificate));
// Set the launch bookmarks activity FAB to launch the bookmarks activity.
launchBookmarksActivityFab.setOnClickListener(v -> {
- // Store the current WebView url and title in the bookmarks activity. // TODO.
- BookmarksActivity.currentWebViewUrl = currentWebView.getUrl();
- BookmarksActivity.currentWebViewTitle = currentWebView.getTitle();
-
// Get a copy of the favorite icon bitmap.
Bitmap favoriteIconBitmap = currentWebView.getFavoriteOrDefaultIcon();
Intent bookmarksIntent = new Intent(getApplicationContext(), BookmarksActivity.class);
// Add the extra information to the intent.
+ bookmarksIntent.putExtra("current_url", currentWebView.getUrl());
+ bookmarksIntent.putExtra("current_title", currentWebView.getTitle());
bookmarksIntent.putExtra("current_folder", currentBookmarksFolder);
bookmarksIntent.putExtra("favorite_icon_byte_array", favoriteIconByteArray);
// Initialize the privacy settings variables.
firstPartyCookiesEnabled = false;
- saveFormDataEnabled = false; // Form data can be removed once the minimum API >= 26.
- nightMode = 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);
// Destroy the bare WebView.
bareWebView.destroy();
-
- // Initialize the user agent array adapter and string array.
- userAgentNamesArray = ArrayAdapter.createFromResource(this, R.array.user_agent_names, R.layout.spinner_item);
- userAgentDataArray = getResources().getStringArray(R.array.user_agent_data);
-
- // Get the intent that started the app.
- Intent launchingIntent = getIntent();
-
- // Get the information from the intent.
- String launchingIntentAction = launchingIntent.getAction();
- Uri launchingIntentUriData = launchingIntent.getData();
-
- // If the intent action is a web search, perform the search.
- if ((launchingIntentAction != null) && launchingIntentAction.equals(Intent.ACTION_WEB_SEARCH)) {
- // Create an encoded URL string.
- String encodedUrlString;
-
- // Sanitize the search input and convert it to a search.
- try {
- encodedUrlString = URLEncoder.encode(launchingIntent.getStringExtra(SearchManager.QUERY), "UTF-8");
- } catch (UnsupportedEncodingException exception) {
- encodedUrlString = "";
- }
-
- // Add the base search URL.
- formattedUrlString = searchURL + encodedUrlString;
- } else if (launchingIntentUriData != null){ // Check to see if the intent contains a new URL.
- // Set the formatted URL string.
- formattedUrlString = launchingIntentUriData.toString();
- }
}
@Override
// Add a new tab.
addTab(null);
+ // Create a URL string.
+ String url;
+
// If the intent action is a web search, perform the search.
if ((intentAction != null) && intentAction.equals(Intent.ACTION_WEB_SEARCH)) {
// Create an encoded URL string.
}
// Add the base search URL.
- formattedUrlString = searchURL + encodedUrlString;
+ url = searchURL + encodedUrlString;
} else { // The intent should contain a URL.
- // Set the formatted URL string.
- formattedUrlString = intentUriData.toString();
+ // Set the intent data as the URL.
+ url = intentUriData.toString();
}
// Load the URL.
- loadUrl(formattedUrlString);
+ loadUrl(url);
// Get a handle for the drawer layout.
DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
// Apply the app settings.
applyAppSettings();
- // Reload the webpage if displaying of images has been disabled in the Settings activity.
+ // Reload the webpage to handle changes to night mode and displaying of images.
if (reloadOnRestart) {
// Reload the WebViews.
for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
// TODO apply to all the tabs.
// Apply the domain settings if returning from the Domains activity.
if (reapplyDomainSettingsOnRestart) {
+ // Reset the current domain name so the domain settings will be reapplied.
+ currentWebView.resetCurrentDomainName();
+
// Reapply the domain settings.
- applyDomainSettings(currentWebView, formattedUrlString, false, true);
+ applyDomainSettings(currentWebView, currentWebView.getUrl(), false, true); // TODO.
- // Reset `reapplyDomainSettingsOnRestart`.
+ // Reset the reapply domain settings on restart tracker.
reapplyDomainSettingsOnRestart = false;
}
- // Load the URL on restart to apply changes to night mode.
+ // Load the URL on restart (used when loading a bookmark.
if (loadUrlOnRestart) {
- // Load the current `formattedUrlString`.
- loadUrl(formattedUrlString);
+ // Load the specified URL.
+ loadUrl(urlToLoadOnRestart);
- // Reset `loadUrlOnRestart.
+ // Reset the load on restart tracker.
loadUrlOnRestart = false;
}
// Inflate the menu. This adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.webview_options_menu, menu);
- // Store a handle for the options menu so it can be used by `onOptionsItemSelected()` and `updatePrivacyIcons`.
+ // Store a handle for the options menu so it can be used by `onOptionsItemSelected()` and `updatePrivacyIcons()`.
optionsMenu = menu;
// Set the initial status of the privacy icons. `false` does not call `invalidateOptionsMenu` as the last step.
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.
- refreshMenuItem = menu.findItem(R.id.refresh);
+ MenuItem refreshMenuItem = menu.findItem(R.id.refresh);
blocklistsMenuItem = menu.findItem(R.id.blocklists);
easyListMenuItem = menu.findItem(R.id.easylist);
easyPrivacyMenuItem = menu.findItem(R.id.easyprivacy);
toggleSaveFormDataMenuItem.setVisible(Build.VERSION.SDK_INT < 26);
clearFormDataMenuItem.setVisible(Build.VERSION.SDK_INT < 26);
+ // 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 show Ad Consent if this is the free flavor.
adConsentMenuItem.setVisible(BuildConfig.FLAVOR.contentEquals("free"));
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
// Get the status of the additional AppBar icons.
- displayAdditionalAppBarIcons = sharedPreferences.getBoolean("display_additional_app_bar_icons", false);
+ boolean displayAdditionalAppBarIcons = sharedPreferences.getBoolean("display_additional_app_bar_icons", 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) {
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
- // Get a handle for the swipe refresh layout.
- SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
-
// Get handles for the menu items.
MenuItem addOrEditDomain = menu.findItem(R.id.add_or_edit_domain);
- 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 firstPartyCookiesMenuItem = menu.findItem(R.id.toggle_first_party_cookies);
+ MenuItem thirdPartyCookiesMenuItem = menu.findItem(R.id.toggle_third_party_cookies);
+ MenuItem domStorageMenuItem = menu.findItem(R.id.toggle_dom_storage);
+ MenuItem saveFormDataMenuItem = menu.findItem(R.id.toggle_save_form_data); // Form data can be removed once the minimum API >= 26.
MenuItem clearDataMenuItem = menu.findItem(R.id.clear_data);
MenuItem clearCookiesMenuItem = menu.findItem(R.id.clear_cookies);
MenuItem clearDOMStorageMenuItem = menu.findItem(R.id.clear_dom_storage);
fontSize = currentWebView.getSettings().getTextZoom();
// Set the status of the menu item checkboxes.
- toggleDomStorageMenuItem.setChecked(currentWebView.getSettings().getDomStorageEnabled());
+ domStorageMenuItem.setChecked(currentWebView.getSettings().getDomStorageEnabled());
+ saveFormDataMenuItem.setChecked(currentWebView.getSettings().getSaveFormData()); // Form data can be removed once the minimum API >= 26.
easyListMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASY_LIST));
easyPrivacyMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASY_PRIVACY));
fanboysAnnoyanceListMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST));
fanboysSocialBlockingListMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST));
ultraPrivacyMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRA_PRIVACY));
blockAllThirdPartyRequestsMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS));
+ swipeToRefreshMenuItem.setChecked(currentWebView.getSwipeToRefresh());
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));
fanboysSocialBlockingListMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST) + " - " + getString(R.string.fanboys_social_blocking_list));
ultraPrivacyMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.ULTRA_PRIVACY) + " - " + getString(R.string.ultraprivacy));
blockAllThirdPartyRequestsMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.THIRD_PARTY_REQUESTS) + " - " + getString(R.string.block_all_third_party_requests));
- }
- // Set the status of the menu item checkboxes.
- toggleFirstPartyCookiesMenuItem.setChecked(firstPartyCookiesEnabled);
- toggleSaveFormDataMenuItem.setChecked(saveFormDataEnabled); // Form data can be removed once the minimum API >= 26.
- swipeToRefreshMenuItem.setChecked(swipeRefreshLayout.isEnabled());
- nightModeMenuItem.setChecked(nightMode);
- proxyThroughOrbotMenuItem.setChecked(proxyThroughOrbot);
+ // Only modify third-party cookies if the API >= 21.
+ if (Build.VERSION.SDK_INT >= 21) {
+ // Set the status of the third-party cookies checkbox.
+ thirdPartyCookiesMenuItem.setChecked(cookieManager.acceptThirdPartyCookies(currentWebView));
- // Only modify third-party cookies if the API >= 21.
- if (Build.VERSION.SDK_INT >= 21) {
- // Set the status of the third-party cookies checkbox.
- toggleThirdPartyCookiesMenuItem.setChecked(cookieManager.acceptThirdPartyCookies(currentWebView));
+ // Enable third-party cookies if first-party cookies are enabled.
+ thirdPartyCookiesMenuItem.setEnabled(firstPartyCookiesEnabled);
+ }
- // Enable third-party cookies if first-party cookies are enabled.
- toggleThirdPartyCookiesMenuItem.setEnabled(firstPartyCookiesEnabled);
+ // Enable DOM Storage if JavaScript is enabled.
+ domStorageMenuItem.setEnabled(currentWebView.getSettings().getJavaScriptEnabled());
}
- // Enable DOM Storage if JavaScript is enabled.
- toggleDomStorageMenuItem.setEnabled(currentWebView.getSettings().getJavaScriptEnabled());
+ // Set the status of the menu item checkboxes.
+ firstPartyCookiesMenuItem.setChecked(firstPartyCookiesEnabled);
+ proxyThroughOrbotMenuItem.setChecked(proxyThroughOrbot);
// Enable Clear Cookies if there are any.
clearCookiesMenuItem.setEnabled(cookieManager.hasCookies());
// Enable Clear Form Data is there is any. This can be removed once the minimum API >= 26.
if (Build.VERSION.SDK_INT < 26) {
- WebViewDatabase mainWebViewDatabase = WebViewDatabase.getInstance(this);
- clearFormDataMenuItem.setEnabled(mainWebViewDatabase.hasFormData());
- } else {
- // Disable clear form data because it is not supported on current version of Android.
- clearFormDataMenuItem.setEnabled(false);
+ // Get the WebView database.
+ WebViewDatabase webViewDatabase = WebViewDatabase.getInstance(this);
+
+ // Enable the clear form data menu item if there is anything to clear.
+ clearFormDataMenuItem.setEnabled(webViewDatabase.hasFormData());
}
// Enable Clear Data if any of the submenu items are enabled.
if (currentWebView.getDomainSettingsApplied()) { // Edit the current domain settings.
// Reapply the domain settings on returning to `MainWebViewActivity`.
reapplyDomainSettingsOnRestart = true;
- currentWebView.resetCurrentDomainName();
// TODO. Move these to `putExtra`. The certificate can be stored as strings.
// Store the current SSL certificate and IP addresses in the domains activity.
// Create an intent to launch the domains activity.
Intent domainsIntent = new Intent(this, DomainsActivity.class);
- // Put extra information instructing the domains activity to directly load the current domain and close on back instead of returning to the domains list.
+ // 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());
// Make it so.
startActivity(domainsIntent);
} else { // Add a new domain.
// Apply the new domain settings on returning to `MainWebViewActivity`.
reapplyDomainSettingsOnRestart = true;
- currentWebView.resetCurrentDomainName();
// Get the current domain
- Uri currentUri = Uri.parse(formattedUrlString);
+ 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`.
// Create an intent to launch the domains activity.
Intent domainsIntent = new Intent(this, DomainsActivity.class);
- // Put extra information instructing the domains activity to directly load the new domain and close on back instead of returning to the domains list.
+ // Add the extra information to the intent.
domainsIntent.putExtra("load_domain", newDomainDatabaseId);
domainsIntent.putExtra("close_on_back", true);
+ domainsIntent.putExtra("current_url", currentWebView.getUrl());
// Make it so.
startActivity(domainsIntent);
// Form data can be removed once the minimum API >= 26.
case R.id.toggle_save_form_data:
// Switch the status of saveFormDataEnabled.
- saveFormDataEnabled = !saveFormDataEnabled;
+ currentWebView.getSettings().setSaveFormData(!currentWebView.getSettings().getSaveFormData());
// Update the menu checkbox.
- menuItem.setChecked(saveFormDataEnabled);
-
- // Apply the new form data status.
- currentWebView.getSettings().setSaveFormData(saveFormDataEnabled);
+ menuItem.setChecked(currentWebView.getSettings().getSaveFormData());
- // Display a `Snackbar`.
- if (saveFormDataEnabled) {
+ // 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();
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);
- // Toggle swipe to refresh.
- swipeRefreshLayout.setEnabled(!swipeRefreshLayout.isEnabled());
+ // Update the swipe refresh layout.
+ if (currentWebView.getSwipeToRefresh()) { // Swipe to refresh is enabled.
+ if (Build.VERSION.SDK_INT >= 23) { // For API >= 23, the status of the scroll refresh listener is continuously updated by the on scroll change listener.
+ // Only enable the swipe refresh layout if the WebView is scrolled to the top.
+ swipeRefreshLayout.setEnabled(currentWebView.getY() == 0);
+ } else { // For API < 23, the swipe refresh layout is always enabled.
+ // Enable the swipe refresh layout.
+ swipeRefreshLayout.setEnabled(true);
+ }
+ } else { // Swipe to refresh is disabled.
+ // Disable the swipe refresh layout.
+ swipeRefreshLayout.setEnabled(false);
+ }
return true;
case R.id.display_images:
case R.id.night_mode:
// Toggle night mode.
- nightMode = !nightMode;
+ currentWebView.setNightMode(!currentWebView.getNightMode());
// Enable or disable JavaScript according to night mode, the global preference, and any domain settings.
- if (nightMode) { // Night mode is enabled, which requires JavaScript.
+ 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(domainSettingsJavaScriptEnabled);
+ currentWebView.getSettings().setJavaScriptEnabled(domainSettingsJavaScriptEnabled); // TODO.
} 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));
// 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);
// Hide the toolbar.
toolbar.setVisibility(View.GONE);
// 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);
// Create an intent to launch the view source activity.
Intent viewSourceIntent = new Intent(this, ViewSourceActivity.class);
- // Add the user agent as an extra to the intent.
+ // Add the variables to the intent.
viewSourceIntent.putExtra("user_agent", currentWebView.getSettings().getUserAgentString());
+ viewSourceIntent.putExtra("current_url", currentWebView.getUrl());
// Make it so.
startActivity(viewSourceIntent);
case R.id.share_url:
// Setup the share string.
- String shareString = currentWebView.getTitle() + " – " + formattedUrlString;
+ String shareString = currentWebView.getTitle() + " – " + currentWebView.getUrl();
// Create the share intent.
Intent shareIntent = new Intent(Intent.ACTION_SEND);
return true;
case R.id.open_with_app:
- openWithApp(formattedUrlString);
+ openWithApp(currentWebView.getUrl());
return true;
case R.id.open_with_browser:
- openWithBrowser(formattedUrlString);
+ openWithBrowser(currentWebView.getUrl());
return true;
case R.id.add_to_homescreen:
// Instantiate the create home screen shortcut dialog.
- DialogFragment createHomeScreenShortcutDialogFragment = CreateHomeScreenShortcutDialog.createDialog(currentWebView.getTitle(), formattedUrlString, currentWebView.getFavoriteOrDefaultIcon());
+ DialogFragment createHomeScreenShortcutDialogFragment = CreateHomeScreenShortcutDialog.createDialog(currentWebView.getTitle(), currentWebView.getUrl(),
+ currentWebView.getFavoriteOrDefaultIcon());
// Show the create home screen shortcut dialog.
createHomeScreenShortcutDialogFragment.show(getSupportFragmentManager(), getString(R.string.create_shortcut));
}
}
- // Clear the formatted URL string.
- formattedUrlString = null;
-
// Clear the custom headers.
customHeaders.clear();
case R.id.back:
if (currentWebView.canGoBack()) {
- // Reset the formatted URL string so the page will load correctly if blocking of third-party requests is enabled.
- formattedUrlString = "";
+ // Reset the current domain name so that navigation works if third-party requests are blocked.
+ currentWebView.resetCurrentDomainName();
// Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded.
navigatingHistory = true;
case R.id.forward:
if (currentWebView.canGoForward()) {
- // Reset the formatted URL string so the page will load correctly if blocking of third-party requests is enabled.
- formattedUrlString = "";
+ // Reset the current domain name so that navigation works if third-party requests are blocked.
+ currentWebView.resetCurrentDomainName();
// Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded.
navigatingHistory = true;
case R.id.domains:
// Set the flag to reapply the domain settings on restart when returning from Domain Settings.
reapplyDomainSettingsOnRestart = true;
- currentWebView.resetCurrentDomainName(); // TODO. Do this for all tabs.
// TODO. Move these to `putExtra`. The certificate can be stored as strings.
// Store the current SSL certificate and IP addresses in the domains activity.
// Launch the domains activity.
Intent domainsIntent = new Intent(this, DomainsActivity.class);
+
+ // Add the extra information to the intent.
+ domainsIntent.putExtra("current_url", currentWebView.getUrl());
+
+ // Make it so.
startActivity(domainsIntent);
break;
// Set the flag to reapply the domain settings on restart when returning from Settings.
reapplyDomainSettingsOnRestart = true;
- currentWebView.resetCurrentDomainName(); // TODO. Do this for all tabs.
// Launch the settings activity.
Intent settingsIntent = new Intent(this, SettingsActivity.class);
@Override
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
- // Store the `HitTestResult`.
+ // Store the hit test result.
final WebView.HitTestResult hitTestResult = currentWebView.getHitTestResult();
- // Create strings.
+ // Create the URL strings.
final String imageUrl;
final String linkUrl;
- // Get a handle for the the clipboard and fragment managers.
+ // Get handles for the the clipboard and fragment managers.
final ClipboardManager clipboardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
FragmentManager fragmentManager = getSupportFragmentManager();
- // Remove the lint errors below that `clipboardManager` might be `null`.
+ // Remove the lint errors below that the clipboard manager might be null.
assert clipboardManager != null;
+ // Process the link according to the type.
switch (hitTestResult.getType()) {
// `SRC_ANCHOR_TYPE` is a link.
case WebView.HitTestResult.SRC_ANCHOR_TYPE:
menu.setHeaderTitle(linkUrl);
// Add a Load URL entry.
- menu.add(R.string.load_url).setOnMenuItemClickListener((MenuItem item) -> {
+ menu.add(R.string.open_in_new_tab).setOnMenuItemClickListener((MenuItem item) -> {
+ // Add a new tab.
+ addTab(null);
+
+ // Load the URL.
loadUrl(linkUrl);
return false;
});
+ // Add an Open with App entry.
+ menu.add(R.string.open_with_app).setOnMenuItemClickListener((MenuItem item) -> {
+ openWithApp(linkUrl);
+ return false;
+ });
+
+ // Add an Open with Browser entry.
+ menu.add(R.string.open_with_browser).setOnMenuItemClickListener((MenuItem item) -> {
+ openWithBrowser(linkUrl);
+ return false;
+ });
+
// Add a Copy URL entry.
menu.add(R.string.copy_url).setOnMenuItemClickListener((MenuItem item) -> {
// Save the link URL in a `ClipData`.
return false;
});
- // Add an Open with App entry.
- menu.add(R.string.open_with_app).setOnMenuItemClickListener((MenuItem item) -> {
- openWithApp(linkUrl);
- return false;
- });
-
- // Add an Open with Browser entry.
- menu.add(R.string.open_with_browser).setOnMenuItemClickListener((MenuItem item) -> {
- openWithBrowser(linkUrl);
- return false;
- });
-
// Add a Cancel entry, which by default closes the context menu.
menu.add(R.string.cancel);
break;
@Override
public void onCreateBookmark(DialogFragment dialogFragment, Bitmap favoriteIconBitmap) {
+ // Get a handle for the bookmarks list view.
+ ListView bookmarksListView = findViewById(R.id.bookmarks_drawer_listview);
+
// Get the views from the dialog fragment.
EditText createBookmarkNameEditText = dialogFragment.getDialog().findViewById(R.id.create_bookmark_name_edittext);
EditText createBookmarkUrlEditText = dialogFragment.getDialog().findViewById(R.id.create_bookmark_url_edittext);
@Override
public void onCreateBookmarkFolder(DialogFragment dialogFragment, Bitmap favoriteIconBitmap) {
+ // Get a handle for the bookmarks list view.
+ ListView bookmarksListView = findViewById(R.id.bookmarks_drawer_listview);
+
// Get handles for the views in the dialog fragment.
EditText createFolderNameEditText = dialogFragment.getDialog().findViewById(R.id.create_folder_name_edittext);
RadioButton defaultFolderIconRadioButton = dialogFragment.getDialog().findViewById(R.id.create_folder_default_icon_radiobutton);
@Override
public void onPinnedMismatchBack() { // TODO. Move this logic to the dialog.
if (currentWebView.canGoBack()) { // There is a back page in the history.
- // Reset the formatted URL string so the page will load correctly if blocking of third-party requests is enabled.
- formattedUrlString = ""; // TODO.
+ // Reset the current domain name so that navigation works if third-party requests are blocked.
+ currentWebView.resetCurrentDomainName();
// Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded.
navigatingHistory = true; // TODO.
@Override
public void onUrlHistoryEntrySelected(int moveBackOrForwardSteps) {
- // Reset the formatted URL string so the page will load correctly if blocking of third-party requests is enabled.
- formattedUrlString = "";
+ // Reset the current domain name so that navigation works if third-party requests are blocked.
+ currentWebView.resetCurrentDomainName();
// Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded.
navigatingHistory = true;
currentWebView.clearHistory();
}
- // Override `onBackPressed` to handle the navigation drawer and `mainWebView`.
+ // Override `onBackPressed` to handle the navigation drawer and and the WebView.
@Override
public void onBackPressed() {
// Get a handle for the drawer layout.
// Load the new folder.
loadBookmarksFolder();
}
-
} else if (currentWebView.canGoBack()) { // There is at least one item in the current WebView history.
- // Reset the formatted URL string so the page will load correctly if blocking of third-party requests is enabled.
- formattedUrlString = "";
+ // Reset the current domain name so that navigation works if third-party requests are blocked.
+ currentWebView.resetCurrentDomainName();
// Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded.
navigatingHistory = true;
// Get the text from urlTextBox and convert it to a string. trim() removes white spaces from the beginning and end of the string.
String unformattedUrlString = urlEditText.getText().toString().trim();
+ // Initialize the formatted URL string.
+ String url = "";
+
// Check to see if `unformattedUrlString` is a valid URL. Otherwise, convert it into a search.
- if (unformattedUrlString.startsWith("content://")) {
+ if (unformattedUrlString.startsWith("content://")) { // This is a Content URL.
// Load the entire content URL.
- formattedUrlString = unformattedUrlString;
- } else if (Patterns.WEB_URL.matcher(unformattedUrlString).matches() || unformattedUrlString.startsWith("http://") || unformattedUrlString.startsWith("https://")
- || unformattedUrlString.startsWith("file://")) {
+ url = unformattedUrlString;
+ } else if (Patterns.WEB_URL.matcher(unformattedUrlString).matches() || unformattedUrlString.startsWith("http://") || unformattedUrlString.startsWith("https://") ||
+ unformattedUrlString.startsWith("file://")) { // This is a standard URL.
// Add `https://` at the beginning if there is no protocol. Otherwise the app will segfault.
if (!unformattedUrlString.startsWith("http") && !unformattedUrlString.startsWith("file://") && !unformattedUrlString.startsWith("content://")) {
unformattedUrlString = "https://" + unformattedUrlString;
String fragment = unformattedUrl != null ? unformattedUrl.getRef() : null;
// Build the URI.
- Uri.Builder formattedUri = new Uri.Builder();
- formattedUri.scheme(scheme).authority(authority).path(path).query(query).fragment(fragment);
+ Uri.Builder uri = new Uri.Builder();
+ uri.scheme(scheme).authority(authority).path(path).query(query).fragment(fragment);
- // Decode `formattedUri` as a `String` in `UTF-8`.
+ // Decode the URI as a UTF-8 string in.
try {
- formattedUrlString = URLDecoder.decode(formattedUri.build().toString(), "UTF-8");
+ url = URLDecoder.decode(uri.build().toString(), "UTF-8");
} catch (UnsupportedEncodingException exception) {
- // Load a blank string.
- formattedUrlString = "";
+ // Do nothing. The formatted URL string will remain blank.
}
- } else if (unformattedUrlString.isEmpty()){ // Load a blank web site.
- // Load a blank string.
- formattedUrlString = "";
- } else { // Search for the contents of the URL box.
+ } else if (!unformattedUrlString.isEmpty()){ // This is not a URL, but rather a search string.
// Create an encoded URL String.
String encodedUrlString;
}
// Add the base search URL.
- formattedUrlString = searchURL + encodedUrlString;
+ url = searchURL + encodedUrlString;
}
// Clear the focus from the URL edit text. Otherwise, proximate typing in the box will retain the colorized formatting instead of being reset during refocus.
urlEditText.clearFocus();
// Make it so.
- loadUrl(formattedUrlString);
+ loadUrl(url);
}
- private void loadUrl(String url) {// Apply any custom domain settings.
- // Set the URL as the formatted URL string so that checking third-party requests works correctly.
- formattedUrlString = url;
-
+ private void loadUrl(String url) {
// Apply the domain settings.
applyDomainSettings(currentWebView, url, true, false);
// 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);
// Delete the contents of `find_on_page_edittext`.
findOnPageEditText.setText(null);
// Show the toolbar.
toolbar.setVisibility(View.VISIBLE);
+ // 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;
+
// Hide the keyboard.
inputMethodManager.hideSoftInputFromWindow(currentWebView.getWindowToken(), 0);
}
// `reloadWebsite` is used if returning from the Domains activity. Otherwise JavaScript might not function correctly if it is newly enabled.
@SuppressLint("SetJavaScriptEnabled")
private boolean applyDomainSettings(NestedScrollWebView nestedScrollWebView, String url, boolean resetFavoriteIcon, boolean reloadWebsite) {
- // Get a handle for the URL relative layout.
- RelativeLayout urlRelativeLayout = findViewById(R.id.url_relativelayout);
-
// Store a copy of the current user agent to track changes for the return boolean.
String initialUserAgent = nestedScrollWebView.getSettings().getUserAgentString();
// Reset the favorite icon if specified.
if (resetFavoriteIcon) {
// Initialize the favorite icon.
- currentWebView.initializeFavoriteIcon();
+ nestedScrollWebView.initializeFavoriteIcon();
// Get a handle for the tab layout.
TabLayout tabLayout = findViewById(R.id.tablayout);
currentTabFavoriteIconImageView.setImageBitmap(Bitmap.createScaledBitmap(nestedScrollWebView.getFavoriteOrDefaultIcon(), 64, 64, true));
}
- // Get a handle for the swipe refresh layout.
- SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
-
// 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);
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);
- nightMode = sharedPreferences.getBoolean("night_mode", false); // TODO.
boolean displayWebpageImages = sharedPreferences.getBoolean("display_webpage_images", true);
// Get a handle for the cookie manager.
CookieManager cookieManager = CookieManager.getInstance();
+ // Get handles for the views.
+ RelativeLayout urlRelativeLayout = findViewById(R.id.url_relativelayout);
+ SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
+
+ // Initialize the user agent array adapter and string array.
+ ArrayAdapter<CharSequence> userAgentNamesArray = ArrayAdapter.createFromResource(this, R.array.user_agent_names, R.layout.spinner_item);
+ String[] userAgentDataArray = getResources().getStringArray(R.array.user_agent_data);
+
if (nestedScrollWebView.getDomainSettingsApplied()) { // The url has custom domain settings.
// Get a cursor for the current host and move it to the first position.
Cursor currentDomainSettingsCursor = domainsDatabaseHelper.getCursorForDomainName(domainNameInDatabase);
boolean domainJavaScriptEnabled = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_JAVASCRIPT)) == 1);
firstPartyCookiesEnabled = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FIRST_PARTY_COOKIES)) == 1); // TODO.
boolean domainThirdPartyCookiesEnabled = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_THIRD_PARTY_COOKIES)) == 1);
- boolean domainDomStorageEnabled = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_DOM_STORAGE)) == 1);
+ nestedScrollWebView.getSettings().setDomStorageEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_DOM_STORAGE)) == 1);
// Form data can be removed once the minimum API >= 26.
- saveFormDataEnabled = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FORM_DATA)) == 1); // TODO.
+ boolean saveFormData = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FORM_DATA)) == 1);
nestedScrollWebView.enableBlocklist(NestedScrollWebView.EASY_LIST,
currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_EASYLIST)) == 1);
nestedScrollWebView.enableBlocklist(NestedScrollWebView.EASY_PRIVACY,
nestedScrollWebView.setPinnedIpAddresses(pinnedHostIpAddresses);
}
- // Set `nightMode` according to `nightModeInt`. If `nightModeInt` is `DomainsDatabaseHelper.NIGHT_MODE_SYSTEM_DEFAULT` the current setting from `sharedPreferences` will be used.
+ // Set night mode according to the night mode int.
switch (nightModeInt) {
+ case DomainsDatabaseHelper.NIGHT_MODE_SYSTEM_DEFAULT:
+ // Set night mode according to the current default.
+ nestedScrollWebView.setNightMode(sharedPreferences.getBoolean("night_mode", false));
+ break;
+
case DomainsDatabaseHelper.NIGHT_MODE_ENABLED:
- nightMode = true; // TODO.
+ // Enable night mode.
+ nestedScrollWebView.setNightMode(true);
break;
case DomainsDatabaseHelper.NIGHT_MODE_DISABLED:
- nightMode = false; // TODO.
+ // Disable night mode.
+ nestedScrollWebView.setNightMode(false);
break;
}
domainSettingsJavaScriptEnabled = domainJavaScriptEnabled;
// Enable JavaScript if night mode is enabled.
- if (nightMode) {
+ if (nestedScrollWebView.getNightMode()) {
// Enable JavaScript.
nestedScrollWebView.getSettings().setJavaScriptEnabled(true);
} else {
// Apply the domain settings.
cookieManager.setAcceptCookie(firstPartyCookiesEnabled); //TODO This could be bad.
- nestedScrollWebView.getSettings().setDomStorageEnabled(domainDomStorageEnabled); // TODO. Move up.
// Apply the form data setting if the API < 26.
if (Build.VERSION.SDK_INT < 26) {
- nestedScrollWebView.getSettings().setSaveFormData(saveFormDataEnabled);
+ nestedScrollWebView.getSettings().setSaveFormData(saveFormData);
}
// Apply the font size.
// Set the user agent.
if (userAgentName.equals(getString(R.string.system_default_user_agent))) { // Use the system default user agent.
// Get the array position of the default user agent name.
- int defaultUserAgentArrayPosition = userAgentNamesArray.getPosition(defaultUserAgentName); // TODO Could this be local.
+ int defaultUserAgentArrayPosition = userAgentNamesArray.getPosition(defaultUserAgentName);
// Set the user agent according to the system default.
switch (defaultUserAgentArrayPosition) {
}
// Set swipe to refresh.
- switch (swipeToRefreshInt) { // TODO. This needs to be set and updated by tab.
+ switch (swipeToRefreshInt) {
case DomainsDatabaseHelper.SWIPE_TO_REFRESH_SYSTEM_DEFAULT:
- // Set swipe to refresh according to the default.
+ // Store the swipe to refresh status in the nested scroll WebView.
+ nestedScrollWebView.setSwipeToRefresh(defaultSwipeToRefresh);
+
+ // Apply swipe to refresh according to the default. This can be removed once the minimum API >= 23 because it is continuously set by an on scroll change listener.
swipeRefreshLayout.setEnabled(defaultSwipeToRefresh);
break;
case DomainsDatabaseHelper.SWIPE_TO_REFRESH_ENABLED:
- // Enable swipe to refresh.
+ // Store the swipe to refresh status in the nested scroll WebView.
+ nestedScrollWebView.setSwipeToRefresh(true);
+
+ // Enable swipe to refresh. This can be removed once the minimum API >= 23 because it is continuously set by an on scroll change listener.
swipeRefreshLayout.setEnabled(true);
break;
case DomainsDatabaseHelper.SWIPE_TO_REFRESH_DISABLED:
- // Disable swipe to refresh.
+ // Store the swipe to refresh status in the nested scroll WebView.
+ nestedScrollWebView.setSwipeToRefresh(false);
+
+ // Disable swipe to refresh. This can be removed once the minimum API >= 23 because it is continuously set by an on scroll change listener.
swipeRefreshLayout.setEnabled(false);
}
firstPartyCookiesEnabled = sharedPreferences.getBoolean("first_party_cookies", false); // TODO.
boolean defaultThirdPartyCookiesEnabled = sharedPreferences.getBoolean("third_party_cookies", false);
nestedScrollWebView.getSettings().setDomStorageEnabled(sharedPreferences.getBoolean("dom_storage", false));
- saveFormDataEnabled = sharedPreferences.getBoolean("save_form_data", false); // Form data can be removed once the minimum API >= 26. // TODO.
+ boolean saveFormData = sharedPreferences.getBoolean("save_form_data", false); // Form data can be removed once the minimum API >= 26.
nestedScrollWebView.enableBlocklist(NestedScrollWebView.EASY_LIST, sharedPreferences.getBoolean("easylist", true));
nestedScrollWebView.enableBlocklist(NestedScrollWebView.EASY_PRIVACY, sharedPreferences.getBoolean("easyprivacy", true));
nestedScrollWebView.enableBlocklist(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST, sharedPreferences.getBoolean("fanboys_annoyance_list", true));
nestedScrollWebView.enableBlocklist(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST, sharedPreferences.getBoolean("fanboys_social_blocking_list", true));
nestedScrollWebView.enableBlocklist(NestedScrollWebView.ULTRA_PRIVACY, 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 (nightMode) {
+ if (nestedScrollWebView.getNightMode()) {
// Enable JavaScript.
nestedScrollWebView.getSettings().setJavaScriptEnabled(true);
} else {
// Apply the default settings.
cookieManager.setAcceptCookie(firstPartyCookiesEnabled); // TODO.
nestedScrollWebView.getSettings().setTextZoom(Integer.valueOf(defaultFontSizeString));
+
+ // Store the swipe to refresh status in the nested scroll WebView.
+ nestedScrollWebView.setSwipeToRefresh(defaultSwipeToRefresh);
+
+ // Apply swipe to refresh according to the default.
swipeRefreshLayout.setEnabled(defaultSwipeToRefresh);
// Apply the form data setting if the API < 26.
if (Build.VERSION.SDK_INT < 26) {
- currentWebView.getSettings().setSaveFormData(saveFormDataEnabled);
+ nestedScrollWebView.getSettings().setSaveFormData(saveFormData);
}
// Reset the pinned variables.
// Set `torHomepageString` as `homepage`.
homepage = torHomepageString;
- // If formattedUrlString is null assign the homepage to it.
- if (formattedUrlString == null) {
- formattedUrlString = homepage;
- }
-
// Set the search URL.
if (torSearchString.equals("Custom URL")) { // Get the custom URL string.
searchURL = torSearchCustomUrlString;
// Set the proxy. `this` refers to the current activity where an `AlertDialog` might be displayed.
OrbotProxyHelper.setProxy(getApplicationContext(), this, "localhost", "8118");
- // Set the `appBar` background to indicate proxying through Orbot is enabled. `this` refers to the context.
+ // Set the `appBar` background to indicate proxying through Orbot is enabled.
if (darkTheme) {
actionBar.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.dark_blue_30));
} else {
// Set `homepageString` as `homepage`.
homepage = homepageString;
- // If formattedUrlString is null assign the homepage to it.
- if (formattedUrlString == null) {
- formattedUrlString = homepage;
- }
-
// Set the search URL.
if (searchString.equals("Custom URL")) { // Get the custom URL string.
searchURL = searchCustomUrlString;
// Reset the proxy to default. The host is `""` and the port is `"0"`.
OrbotProxyHelper.setProxy(getApplicationContext(), this, "", "0");
- // Set the default `appBar` background. `this` refers to the context.
+ // Set the default `appBar` background.
if (darkTheme) {
actionBar.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.gray_900));
} else {
}
private void updatePrivacyIcons(boolean runInvalidateOptionsMenu) {
- // Only update the privacy icons if the options menu has already been populated.
- if (optionsMenu != null) {
+ // Only update the privacy icons if the options menu and the current WebView have already been populated.
+ if ((optionsMenu != null) && (currentWebView != null)) {
// Get handles for the menu items.
MenuItem privacyMenuItem = optionsMenu.findItem(R.id.toggle_javascript);
MenuItem firstPartyCookiesMenuItem = optionsMenu.findItem(R.id.toggle_first_party_cookies);
}
};
- // Populate the `ListView` with the adapter.
+ // Get a handle for the bookmarks list view.
+ ListView bookmarksListView = findViewById(R.id.bookmarks_drawer_listview);
+
+ // Populate the list view with the adapter.
bookmarksListView.setAdapter(bookmarksCursorAdapter);
+ // Get a handle for the bookmarks title text view.
+ TextView bookmarksTitleTextView = findViewById(R.id.bookmarks_title_textview);
+
// Set the bookmarks drawer title.
if (currentBookmarksFolder.isEmpty()) {
bookmarksTitleTextView.setText(R.string.bookmarks);
// Get handles for the URL views.
RelativeLayout urlRelativeLayout = findViewById(R.id.url_relativelayout);
EditText urlEditText = findViewById(R.id.url_edittext);
+ SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
+
+ //Stop the swipe to refresh indicator if it is running
+ swipeRefreshLayout.setRefreshing(false);
// Get the WebView tab fragment.
WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(pageNumber);
// Store the current WebView.
currentWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
+ // Update the status of swipe to refresh.
+ if (currentWebView.getSwipeToRefresh()) { // Swipe to refresh is enabled.
+ if (Build.VERSION.SDK_INT >= 23) { // For API >= 23, swipe refresh layout is continuously updated with an on scroll change listener and only enabled if the WebView is scrolled to the top.
+ // Enable the swipe refresh layout if the WebView is scrolled all the way to the top.
+ swipeRefreshLayout.setEnabled(currentWebView.getY() == 0);
+ } else {
+ // Enable the swipe refresh layout.
+ swipeRefreshLayout.setEnabled(true);
+ }
+ } else { // Swipe to refresh is disabled.
+ // Disable the swipe refresh layout.
+ swipeRefreshLayout.setEnabled(false);
+ }
+
// Update the privacy icons. `true` redraws the icons in the app bar.
updatePrivacyIcons(true);
- // Store the current formatted URL string.
- formattedUrlString = currentWebView.getUrl();
-
// Clear the focus from the URL text box.
urlEditText.clearFocus();
+ // 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;
+
// Hide the soft keyboard.
inputMethodManager.hideSoftInputFromWindow(currentWebView.getWindowToken(), 0);
// Display the current URL in the URL text box.
- urlEditText.setText(formattedUrlString);
+ urlEditText.setText(currentWebView.getUrl());
// Highlight the URL text.
highlightUrlText();
// Get a handle for the activity
Activity activity = this;
+ // 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;
+
// Get a handle for the shared preferences.
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
}
});
+ if (Build.VERSION.SDK_INT >= 23) {
+ nestedScrollWebView.setOnScrollChangeListener((View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) -> {
+ // Update the status of swipe to refresh if it is enabled.
+ if (nestedScrollWebView.getSwipeToRefresh()) {
+ // Only enable swipe to refresh if the WebView is scrolled to the top.
+ swipeRefreshLayout.setEnabled(scrollY == 0);
+ }
+ });
+ }
+
// 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 (nightMode) {
+ if (nestedScrollWebView.getNightMode()) {
// `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.
// Hide the progress bar.
progressBar.setVisibility(View.GONE);
- // Display `mainWebView` 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.
- if (!nightMode) {
+ // 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.
+ if (!nestedScrollWebView.getNightMode()) {
nestedScrollWebView.setVisibility(View.VISIBLE);
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith("http")) { // Load the URL in Privacy Browser.
- // Reset the formatted URL string so the page will load correctly if blocking of third-party requests is enabled.
- formattedUrlString = "";
-
- // Apply the domain settings for the new URL. `applyDomainSettings` doesn't do anything if the domain has not changed.
+ // Apply the domain settings for the new URL. This doesn't do anything if the domain has not changed.
boolean userAgentChanged = applyDomainSettings(nestedScrollWebView, url, true, false);
// Check if the user agent has changed.
// Initialize the third party request tracker.
boolean isThirdPartyRequest = false;
- // Initialize the current domain string.
- String currentDomain = "";
+ // Get the current URL. `.getUrl()` throws an error because operations on the WebView cannot be made from this thread.
+ String currentBaseDomain = nestedScrollWebView.getCurrentDomainName();
- // Nobody is happy when comparing null strings.
- if (!(formattedUrlString == null) && !(url == null)) {
- // Get the domain strings to URIs.
- Uri currentDomainUri = Uri.parse(formattedUrlString);
- Uri requestDomainUri = Uri.parse(url);
+ // Store a copy of the current domain for use in later requests.
+ String currentDomain = currentBaseDomain;
- // Get the domain host names.
- String currentBaseDomain = currentDomainUri.getHost();
- String requestBaseDomain = requestDomainUri.getHost();
+ // Nobody is happy when comparing null strings.
+ if ((currentBaseDomain != null) && (url != null)) {
+ // Convert the request URL to a URI.
+ Uri requestUri = Uri.parse(url);
- // Update the current domain variable.
- currentDomain = currentBaseDomain;
+ // Get the request host name.
+ String requestBaseDomain = requestUri.getHost();
- // Only compare the current base domain and the request base domain if neither is null.
- if (!(currentBaseDomain == null) && !(requestBaseDomain == null)) {
+ // Only check for third-party requests if the current base domain is not empty and the request domain is not null.
+ if (!currentBaseDomain.isEmpty() && (requestBaseDomain != null)) {
// Determine the current base domain.
while (currentBaseDomain.indexOf(".", currentBaseDomain.indexOf(".") + 1) > 0) { // There is at least one subdomain.
// Remove the first subdomain.
nestedScrollWebView.resetRequestsCounters();
// If night mode is enabled, hide `mainWebView` until after the night mode CSS is applied.
- if (nightMode) {
+ if (nestedScrollWebView.getNightMode()) {
nestedScrollWebView.setVisibility(View.INVISIBLE);
}
// Check to see if Privacy Browser is waiting on Orbot.
if (!waitingForOrbot) { // Process the URL.
- // The formatted URL string must be updated at the beginning of the load, so that if the user toggles JavaScript during the load the new website is reloaded.
- formattedUrlString = url;
-
// Display the formatted URL text.
- urlEditText.setText(formattedUrlString);
+ urlEditText.setText(url);
// Apply text highlighting to `urlTextBox`.
highlightUrlText();
- // Get a URI for the current URL.
- Uri currentUri = Uri.parse(formattedUrlString);
-
// Reset the list of host IP addresses.
nestedScrollWebView.clearCurrentIpAddresses();
+ // Get a URI for the current URL.
+ Uri currentUri = Uri.parse(url);
+
// Get the IP addresses for the host.
new GetHostIpAddresses(activity, getSupportFragmentManager(), nestedScrollWebView).execute(currentUri.getHost());
// Manually load the URL if the user agent has changed, which will have caused the previous URL to be reloaded.
if (userAgentChanged) {
- loadUrl(formattedUrlString);
+ loadUrl(url);
}
}
- // Replace Refresh with Stop if the menu item has been created. (The WebView typically begins loading before the menu items are instantiated.)
- if (refreshMenuItem != null) {
+ // Replace Refresh with Stop if the options menu has been created. (The WebView typically begins loading before the menu items are instantiated.)
+ if (optionsMenu != null) {
+ // Get a handle for the refresh menu item.
+ MenuItem refreshMenuItem = optionsMenu.findItem(R.id.refresh);
+
// Set the title.
refreshMenuItem.setTitle(R.string.stop);
+ // Get the status of the additional AppBar icons.
+ boolean displayAdditionalAppBarIcons = sharedPreferences.getBoolean("display_additional_app_bar_icons", false);
+
// If the icon is displayed in the AppBar, set it according to the theme.
if (displayAdditionalAppBarIcons) {
if (darkTheme) {
}
}
- // It is necessary to update `formattedUrlString` and `urlTextBox` after the page finishes loading because the final URL can change during load.
@Override
public void onPageFinished(WebView view, String url) {
// Reset the wide view port if it has been turned off by the waiting for Orbot message.
CookieManager.getInstance().flush();
}
- // Update the Refresh menu item if it has been created.
- if (refreshMenuItem != null) {
+ // Update the Refresh menu item if the options menu has been created.
+ if (optionsMenu != null) {
+ // Get a handle for the refresh menu item.
+ MenuItem refreshMenuItem = optionsMenu.findItem(R.id.refresh);
+
// Reset the Refresh title.
refreshMenuItem.setTitle(R.string.refresh);
+ // Get the status of the additional AppBar icons.
+ boolean displayAdditionalAppBarIcons = sharedPreferences.getBoolean("display_additional_app_bar_icons", false);
+
// If the icon is displayed in the AppBar, reset it according to the theme.
if (displayAdditionalAppBarIcons) {
if (darkTheme) {
// Update the URL text box and apply domain settings if not waiting on Orbot.
if (!waitingForOrbot) {
// Check to see if `WebView` has set `url` to be `about:blank`.
- if (url.equals("about:blank")) { // `WebView` is blank, so `formattedUrlString` should be `""` and `urlTextBox` should display a hint.
- // Set `formattedUrlString` to `""`.
- formattedUrlString = "";
-
- urlEditText.setText(formattedUrlString);
+ if (url.equals("about:blank")) { // The WebView is blank.
+ // Display the hint in the URL edit text.
+ urlEditText.setText("");
// Request focus for `urlTextBox`.
urlEditText.requestFocus();
// Display the keyboard.
inputMethodManager.showSoftInput(urlEditText, 0);
- // Apply the domain settings. This clears any settings from the previous domain.
- applyDomainSettings(nestedScrollWebView, formattedUrlString, true, false);
- } else { // `WebView` has loaded a webpage.
- // Set the formatted URL string. Getting the URL from the WebView instead of using the one provided by `onPageFinished` makes websites like YouTube function correctly.
- formattedUrlString = nestedScrollWebView.getUrl();
+ // Hide the WebView, which causes the default background color to be displayed according to the theme.
+ nestedScrollWebView.setVisibility(View.GONE);
+ // Apply the domain settings. This clears any settings from the previous domain.
+ applyDomainSettings(nestedScrollWebView, "", true, false);
+ } else { // The WebView has loaded a webpage.
// Only update the URL text box if the user is not typing in it.
if (!urlEditText.hasFocus()) {
- // Display the formatted URL text.
- urlEditText.setText(formattedUrlString);
+ // Display the final URL. Getting the URL from the WebView instead of using the one provided by `onPageFinished` makes websites like YouTube function correctly.
+ urlEditText.setText(nestedScrollWebView.getUrl());
// Apply text highlighting to `urlTextBox`.
highlightUrlText();
// Load the website if not waiting for Orbot to connect.
if (!waitingForOrbot) {
- loadUrl(formattedUrlString);
+ // Get the intent that started the app.
+ Intent launchingIntent = getIntent();
+
+ // Get the information from the intent.
+ String launchingIntentAction = launchingIntent.getAction();
+ Uri launchingIntentUriData = launchingIntent.getData();
+
+ // If the intent action is a web search, perform the search.
+ if ((launchingIntentAction != null) && launchingIntentAction.equals(Intent.ACTION_WEB_SEARCH)) {
+ // Create an encoded URL string.
+ String encodedUrlString;
+
+ // Sanitize the search input and convert it to a search.
+ try {
+ encodedUrlString = URLEncoder.encode(launchingIntent.getStringExtra(SearchManager.QUERY), "UTF-8");
+ } catch (UnsupportedEncodingException exception) {
+ encodedUrlString = "";
+ }
+
+ // Load the completed search URL.
+ loadUrl(searchURL + encodedUrlString);
+ } else if (launchingIntentUriData != null){ // Check to see if the intent contains a new URL.
+ // Load the URL from the intent.
+ loadUrl(launchingIntentUriData.toString());
+ } else { // The is no URL in the intent.
+ // Load the homepage.
+ loadUrl(homepage);
+ }
}
}
}