/*
- * Copyright © 2015-2020 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2015-2021 Soren Stoutner <soren@stoutner.com>.
*
* Download cookie code contributed 2017 Hendrik Knackstedt. Copyright assigned to Soren Stoutner <soren@stoutner.com>.
*
private RelativeLayout mainContentRelativeLayout;
private AppBarLayout appBarLayout;
private Toolbar toolbar;
+ private RelativeLayout urlRelativeLayout;
+ private EditText urlEditText;
private ActionBar actionBar;
private LinearLayout findOnPageLinearLayout;
private LinearLayout tabsLinearLayout;
actionBar.setCustomView(R.layout.url_app_bar);
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
+ // Get handles for the views in the URL app bar.
+ urlRelativeLayout = findViewById(R.id.url_relativelayout);
+ urlEditText = findViewById(R.id.url_edittext);
+
// Create the hamburger icon at the start of the AppBar.
actionBarDrawerToggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.open_navigation_drawer, R.string.close_navigation_drawer);
updatePrivacyIcons(true);
}
- // `onResume()` runs after `onStart()`, which runs after `onCreate()` and `onRestart()`.
+ // `onStart()` runs after `onCreate()` or `onRestart()`. This is used instead of `onResume()` so the commands aren't called every time the screen is partially hidden.
@Override
- public void onResume() {
+ public void onStart() {
// Run the default commands.
- super.onResume();
+ super.onStart();
// Resume any WebViews.
for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
}
}
+ // `onStop()` runs after `onPause()`. It is used instead of `onPause()` so the commands are not called every time the screen is partially hidden.
@Override
- public void onPause() {
+ public void onStop() {
// Run the default commands.
- super.onPause();
+ super.onStop();
for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
// Get the WebView tab fragment.
// 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);
+ swipeRefreshLayout.setEnabled(currentWebView.getScrollY() == 0);
} else { // Swipe to refresh is disabled.
// Disable the swipe refresh layout.
swipeRefreshLayout.setEnabled(false);
PrintDocumentAdapter printDocumentAdapter = currentWebView.createPrintDocumentAdapter();
// Print the document.
- printManager.print(getString(R.string.privacy_browser_web_page), printDocumentAdapter, null);
+ printManager.print(getString(R.string.privacy_browser_webpage), printDocumentAdapter, null);
// Consume the event.
return true;
assert dialog != null;
// Get handles for the views in the dialog fragment.
- EditText createFolderNameEditText = dialog.findViewById(R.id.create_folder_name_edittext);
- RadioButton defaultFolderIconRadioButton = dialog.findViewById(R.id.create_folder_default_icon_radiobutton);
- ImageView folderIconImageView = dialog.findViewById(R.id.create_folder_default_icon);
+ EditText folderNameEditText = dialog.findViewById(R.id.folder_name_edittext);
+ RadioButton defaultIconRadioButton = dialog.findViewById(R.id.default_icon_radiobutton);
+ ImageView defaultIconImageView = dialog.findViewById(R.id.default_icon_imageview);
// Get new folder name string.
- String folderNameString = createFolderNameEditText.getText().toString();
+ String folderNameString = folderNameEditText.getText().toString();
// Create a folder icon bitmap.
Bitmap folderIconBitmap;
// Set the folder icon bitmap according to the dialog.
- if (defaultFolderIconRadioButton.isChecked()) { // Use the default folder icon.
+ if (defaultIconRadioButton.isChecked()) { // Use the default folder icon.
// Get the default folder icon drawable.
- Drawable folderIconDrawable = folderIconImageView.getDrawable();
+ Drawable folderIconDrawable = defaultIconImageView.getDrawable();
// Convert the folder icon drawable to a bitmap drawable.
BitmapDrawable folderIconBitmapDrawable = (BitmapDrawable) folderIconDrawable;
}
@Override
- public void onSaveBookmarkFolder(DialogFragment dialogFragment, int selectedFolderDatabaseId, Bitmap favoriteIconBitmap) {
+ public void onSaveBookmarkFolder(DialogFragment dialogFragment, int selectedFolderDatabaseId, @NonNull Bitmap favoriteIconBitmap) {
// Remove the incorrect lint warning below that the dialog fragment might be null.
assert dialogFragment != null;
assert dialog != null;
// 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);
- ImageView defaultFolderIconImageView = dialog.findViewById(R.id.edit_folder_default_icon_imageview);
+ RadioButton currentFolderIconRadioButton = dialog.findViewById(R.id.current_icon_radiobutton);
+ RadioButton defaultFolderIconRadioButton = dialog.findViewById(R.id.default_icon_radiobutton);
+ ImageView defaultFolderIconImageView = dialog.findViewById(R.id.default_icon_imageview);
+ EditText editFolderNameEditText = dialog.findViewById(R.id.folder_name_edittext);
// Get the new folder name.
String newFolderNameString = editFolderNameEditText.getText().toString();
// Update the folder icon in the database.
bookmarksDatabaseHelper.updateFolder(selectedFolderDatabaseId, newFolderIconByteArray);
} else { // The folder icon and the name have changed.
- // Get the new folder icon `Bitmap`.
+ // Get the new folder icon bitmap.
Bitmap folderIconBitmap;
if (defaultFolderIconRadioButton.isChecked()) {
// Get the default folder icon drawable.
}
private void loadUrlFromTextBox() {
- // Get a handle for the URL edit text.
- EditText urlEditText = findViewById(R.id.url_edittext);
-
// 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();
assert dialog != null;
// Get a handle for the edit texts.
- EditText urlEditText = dialog.findViewById(R.id.url_edittext);
+ EditText dialogUrlEditText = dialog.findViewById(R.id.url_edittext);
EditText fileNameEditText = dialog.findViewById(R.id.file_name_edittext);
// Store the URL.
saveWebpageUrl = originalUrlString;
} else {
// Get the URL from the edit text, which may have been modified.
- saveWebpageUrl = urlEditText.getText().toString();
+ saveWebpageUrl = dialogUrlEditText.getText().toString();
}
// Get the file path from the edit text.
redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_900));
}
- // Get handles for the URL views.
- EditText urlEditText = findViewById(R.id.url_edittext);
-
// Remove the formatting from the URL edit text when the user is editing the text.
urlEditText.setOnFocusChangeListener((View v, boolean hasFocus) -> {
if (hasFocus) { // The user is editing the URL text box.
@Override
public void onDrawerClosed(@NonNull View drawerView) {
+ // Reset the drawer icon when the drawer is closed. Otherwise, it is an arrow if the drawer is open when the app is restarted.
+ actionBarDrawerToggle.syncState();
}
@Override
// 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);
// Update the swipe refresh layout.
if (defaultSwipeToRefresh) { // 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);
+ swipeRefreshLayout.setEnabled(currentWebView.getScrollY() == 0);
} else { // Swipe to refresh is disabled.
// Disable the swipe refresh layout.
swipeRefreshLayout.setEnabled(false);
nestedScrollWebView.setSwipeToRefresh(true);
// 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);
+ swipeRefreshLayout.setEnabled(currentWebView.getScrollY() == 0);
break;
case DomainsDatabaseHelper.DISABLED:
// Update the swipe refresh layout.
if (defaultSwipeToRefresh) { // 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);
+ swipeRefreshLayout.setEnabled(currentWebView.getScrollY() == 0);
} else { // Swipe to refresh is disabled.
// Disable the swipe refresh layout.
swipeRefreshLayout.setEnabled(false);
// Set the loading of webpage images.
nestedScrollWebView.getSettings().setLoadsImagesAutomatically(displayWebpageImages);
- // Set a transparent background on URL edit text.
+ // Set a transparent background on the URL relative layout.
urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.color.transparent, null));
}
}
private void highlightUrlText() {
- // Get a handle for the URL edit text.
- EditText urlEditText = findViewById(R.id.url_edittext);
-
// Only highlight the URL text if the box is not currently selected.
if (!urlEditText.hasFocus()) {
// Get the URL string.
}
private void addNewTab(String url, boolean moveToTab) {
+ // Clear the focus from the URL edit text, so that it will be populated with the information from the new tab.
+ urlEditText.clearFocus();
+
// 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();
}
private void closeCurrentTab() {
- // 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();
// Delete the current tab.
tabLayout.removeTabAt(currentTabNumber);
- // Delete the current page. If the selected page number did not change during the delete, it will return true, meaning that the current WebView must be reset.
+ // Delete the current page. If the selected page number did not change during the delete (because the newly selected tab has has same number as the previously deleted tab), it will return true,
+ // meaning that the current WebView must be reset. Otherwise it will happen automatically as the selected tab number changes.
if (webViewPagerAdapter.deletePage(currentTabNumber, webViewPager)) {
setCurrentWebView(currentTabNumber);
}
-
- // Expand the app bar if it is currently collapsed.
- appBarLayout.setExpanded(true);
}
private void saveWebpageArchive(String filePath) {
// Get the WebView tab fragment.
WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i);
- // Get the fragment view.
- View fragmentView = webViewTabFragment.getView();
+ // Get the WebView fragment view.
+ View webViewFragmentView = webViewTabFragment.getView();
// Only clear the cache if the WebView exists.
- if (fragmentView != null) {
+ if (webViewFragmentView != null) {
// Get the nested scroll WebView from the tab fragment.
- NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
+ NestedScrollWebView nestedScrollWebView = webViewFragmentView.findViewById(R.id.nestedscroll_webview);
// Clear the cache for this WebView.
nestedScrollWebView.clearCache(true);
// Get the WebView tab fragment.
WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i);
- // Get the fragment view.
- View fragmentView = webViewTabFragment.getView();
+ // Get the WebView frame layout.
+ FrameLayout webViewFrameLayout = (FrameLayout) webViewTabFragment.getView();
// Only wipe out the WebView if it exists.
- if (fragmentView != null) {
+ if (webViewFrameLayout != null) {
// Get the nested scroll WebView from the tab fragment.
- NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
+ NestedScrollWebView nestedScrollWebView = webViewFrameLayout.findViewById(R.id.nestedscroll_webview);
// Clear SSL certificate preferences for this WebView.
nestedScrollWebView.clearSslPreferences();
// Clear the back/forward history for this WebView.
nestedScrollWebView.clearHistory();
+ // Remove all the views from the frame layout.
+ webViewFrameLayout.removeAllViews();
+
// Destroy the internal state of the WebView.
nestedScrollWebView.destroy();
}
}
private void setCurrentWebView(int pageNumber) {
- // 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);
WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(pageNumber);
// Get the fragment view.
- View fragmentView = webViewTabFragment.getView();
+ View webViewFragmentView = webViewTabFragment.getView();
// Set the current WebView if the fragment view is not null.
- if (fragmentView != null) { // The fragment has been populated.
+ if (webViewFragmentView != null) { // The fragment has been populated.
// Store the current WebView.
- currentWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
+ currentWebView = webViewFragmentView.findViewById(R.id.nestedscroll_webview);
// Update the status of swipe to refresh.
if (currentWebView.getSwipeToRefresh()) { // Swipe to refresh is enabled.
// Enable the swipe refresh layout if the WebView is scrolled all the way to the top. It is updated every time the scroll changes.
- swipeRefreshLayout.setEnabled(currentWebView.getY() == 0);
+ swipeRefreshLayout.setEnabled(currentWebView.getScrollY() == 0);
} else { // Swipe to refresh is disabled.
// Disable the swipe refresh layout.
swipeRefreshLayout.setEnabled(false);
}
}
- // Get handles for the activity views.
- EditText urlEditText = findViewById(R.id.url_edittext);
-
// Get a handle for the activity
Activity activity = this;
// Get the file name from the content disposition.
String fileNameString = PrepareSaveDialog.getFileNameFromHeaders(this, contentDisposition, mimetype, downloadUrl);
+ // Prevent the dialog from displaying if the app window is not visible.
+ // The download listener continues to function even when the WebView is paused. Attempting to display a dialog in that state leads to a crash.
+ while (!activity.getWindow().isActive()) {
+ try {
+ // The window is not active. Wait 1 second.
+ wait(1000);
+ } catch (InterruptedException e) {
+ // Do nothing.
+ }
+ }
+
// Instantiate the save dialog.
DialogFragment saveDialogFragment = SaveWebpageDialog.saveWebpage(StoragePermissionDialog.SAVE_URL, downloadUrl, formattedFileSizeString, fileNameString, userAgent,
nestedScrollWebView.getAcceptFirstPartyCookies());
// Store the SSL error handler.
nestedScrollWebView.setSslErrorHandler(handler);
+ // Prevent the dialog from displaying if the app window is not visible.
+ // The SSL error handler continues to function even when the WebView is paused. Attempting to display a dialog in that state leads to a crash.
+ while (!activity.getWindow().isActive()) {
+ try {
+ // The window is not active. Wait 1 second.
+ wait(1000);
+ } catch (InterruptedException e) {
+ // Do nothing.
+ }
+ }
+
// Instantiate an SSL certificate error alert dialog.
DialogFragment sslCertificateErrorDialogFragment = SslCertificateErrorDialog.displayDialog(error, nestedScrollWebView.getWebViewFragmentId());