X-Git-Url: https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=blobdiff_plain;f=app%2Fsrc%2Fmain%2Fjava%2Fcom%2Fstoutner%2Fprivacybrowser%2Factivities%2FMainWebViewActivity.java;h=c0f2b824ffd31f99de096b907f9cc3ea75941e12;hp=fd357a6a4ed478550916b88ddc639026720590e0;hb=ff636c77836983a078f33e60616204518dab61d1;hpb=9d5e4c56326502b6b74e8f3e463275f5c1e176cc diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java index fd357a6a..c0f2b824 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java @@ -168,31 +168,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `allowScreenshots` is public static so it can be accessed from everywhere. It is also used in `onCreate()`. public static boolean allowScreenshots; - // `favoriteIconBitmap` is public static so it can be accessed from `BookmarksActivity`, `BookmarksDatabaseViewActivity`, `CreateBookmarkFolderDialog`, - // `EditBookmarkDialog`, `EditBookmarkFolderDialog`, `EditBookmarkDatabaseViewDialog`, and `ViewSslCertificateDialog`. It is also used in `onCreate()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, - // `onCreateHomeScreenShortcut()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and `applyDomainSettings()`. - public static Bitmap favoriteIconBitmap; - - // `favoriteIconDefaultBitmap` public static so it can be accessed from `PinnedMismatchDialog`. It is also used in `onCreate()` and `applyDomainSettings`. - public static Bitmap favoriteIconDefaultBitmap; - // 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; - // TODO. We are going to have to move this to the NestedScrollWebView. - // The URL loading tracker is public static so it can be accessed from `GetHostIpAddresses`. - // It is also used in `onCreate()`, `onCreateOptionsMenu()`, `loadUrl()`, `applyDomainSettings()`, and `GetHostIpAddresses`. - public static boolean urlIsLoading; - // `orbotStatus` is public static so it can be accessed from `OrbotProxyHelper`. It is also used in `onCreate()`, `onResume()`, and `applyProxyThroughOrbot()`. public static String orbotStatus; - // TODO. - // `appliedUserAgentString` is public static so it can be accessed from `ViewSourceActivity`. It is also used in `applyDomainSettings()`. - public static String appliedUserAgentString; - // The WebView pager adapter is accessed from `PinnedMismatchDialog`. It is also used in `onCreate()`, `onResume()`, and `addTab()`. public static WebViewPagerAdapter webViewPagerAdapter; @@ -212,10 +195,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook public static String fanboysSocialVersion; public static String ultraPrivacyVersion; - // `blockAllThirdPartyRequests` is public static so it can be accessed from `RequestsActivity`. - // It is also used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyAppSettings()` - public static boolean blockAllThirdPartyRequests; - // `currentBookmarksFolder` is public static so it can be accessed from `BookmarksActivity`. It is also used in `onCreate()`, `onBackPressed()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, // `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and `loadBookmarksFolder()`. public static String currentBookmarksFolder; @@ -231,33 +210,18 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `navigatingHistory` is used in `onCreate()`, `onNavigationItemSelected()`, `onSslMismatchBack()`, and `applyDomainSettings()`. - private boolean navigatingHistory; + private boolean navigatingHistory; // TODO. // The current WebView is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onRestart()`, `onCreateContextMenu()`, `findPreviousOnPage()`, // `findNextOnPage()`, `closeFindOnPage()`, `loadUrlFromTextBox()`, `onSslMismatchBack()`, `applyProxyThroughOrbot()`, and `applyDomainSettings()`. private NestedScrollWebView currentWebView; - // `fullScreenVideoFrameLayout` is used in `onCreate()` and `onConfigurationChanged()`. - private FrameLayout fullScreenVideoFrameLayout; - - // `cookieManager` is used in `onCreate()`, `onOptionsItemSelected()`, and `onNavigationItemSelected()`, `loadUrlFromTextBox()`, `onDownloadImage()`, `onDownloadFile()`, and `onRestart()`. - private CookieManager cookieManager; - // `customHeader` is used in `onCreate()`, `onOptionsItemSelected()`, `onCreateContextMenu()`, and `loadUrl()`. private final Map customHeaders = new HashMap<>(); - // `javaScriptEnabled` is also used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, `applyDomainSettings()`, and `updatePrivacyIcons()`. - private boolean javaScriptEnabled; - // `firstPartyCookiesEnabled` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, `onDownloadImage()`, `onDownloadFile()`, and `applyDomainSettings()`. private boolean firstPartyCookiesEnabled; - // `thirdPartyCookiesEnabled` used in `onCreate()`, `onPrepareOptionsMenu()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyDomainSettings()`. - private boolean thirdPartyCookiesEnabled; - - // `domStorageEnabled` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyDomainSettings()`. - private boolean domStorageEnabled; - // `saveFormDataEnabled` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyDomainSettings()`. It can be removed once the minimum API >= 26. private boolean saveFormDataEnabled; @@ -266,19 +230,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook private boolean nightMode; // 'homepage' is used in `onCreate()`, `onNavigationItemSelected()`, and `applyProxyThroughOrbot()`. - private String homepage; + private String homepage; // TODO ? // `searchURL` is used in `loadURLFromTextBox()` and `applyProxyThroughOrbot()`. - private String searchURL; - - // `mainMenu` is used in `onCreateOptionsMenu()` and `updatePrivacyIcons()`. - private Menu mainMenu; + private String searchURL; // TODO ? - // `refreshMenuItem` is used in `onCreate()` and `onCreateOptionsMenu()`. - private MenuItem refreshMenuItem; + // The options menu is set in `onCreateOptionsMenu()` and used in `onOptionsItemSelected()` and `updatePrivacyIcons()`. + private Menu optionsMenu; - // The navigation requests menu item is used in `onCreate()` and accessed from `WebViewPagerAdapter`. - private MenuItem navigationRequestsMenuItem; + // 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`. @@ -291,7 +253,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook private ArrayList> fanboysSocialList; private ArrayList> ultraPrivacy; - // The blocklist menu items are used in `onCreate()`, `onCreateOptionsMenu()`, and `onPrepareOptionsMenu()`. + // The blocklist menu items are used in `onCreateOptionsMenu()`, `onPrepareOptionsMenu()`, and `initializeWebView()`. // TODO. private MenuItem blocklistsMenuItem; private MenuItem easyListMenuItem; private MenuItem easyPrivacyMenuItem; @@ -300,36 +262,23 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook private MenuItem ultraPrivacyMenuItem; private MenuItem blockAllThirdPartyRequestsMenuItem; - // The blocklist variables are used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyAppSettings()`. - private boolean easyListEnabled; - private boolean easyPrivacyEnabled; - private boolean fanboysAnnoyanceListEnabled; - private boolean fanboysSocialBlockingListEnabled; - private boolean ultraPrivacyEnabled; - // `webViewDefaultUserAgent` is used in `onCreate()` and `onPrepareOptionsMenu()`. private String webViewDefaultUserAgent; - // `defaultCustomUserAgentString` is used in `onPrepareOptionsMenu()` and `applyDomainSettings()`. - private String defaultCustomUserAgentString; - - // `privacyBrowserRuntime` is used in `onCreate()`, `onOptionsItemSelected()`, and `applyAppSettings()`. - private Runtime privacyBrowserRuntime; - // `proxyThroughOrbot` is used in `onRestart()`, `onOptionsItemSelected()`, `applyAppSettings()`, and `applyProxyThroughOrbot()`. private boolean proxyThroughOrbot; // `incognitoModeEnabled` is used in `onCreate()` and `applyAppSettings()`. - private boolean incognitoModeEnabled; + private boolean incognitoModeEnabled; // TODO. // `fullScreenBrowsingModeEnabled` is used in `onCreate()` and `applyAppSettings()`. - private boolean fullScreenBrowsingModeEnabled; + private boolean fullScreenBrowsingModeEnabled; // TODO. // `inFullScreenBrowsingMode` is used in `onCreate()`, `onConfigurationChanged()`, and `applyAppSettings()`. private boolean inFullScreenBrowsingMode; // Hide app bar is used in `onCreate()` and `applyAppSettings()`. - private boolean hideAppBar; + private boolean hideAppBar; // TODO. // `reapplyDomainSettingsOnRestart` is used in `onCreate()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onRestart()`, and `onAddDomain()`, . private boolean reapplyDomainSettingsOnRestart; @@ -341,10 +290,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook private boolean displayingFullScreenVideo; // `downloadWithExternalApp` is used in `onCreate()`, `onCreateContextMenu()`, and `applyDomainSettings()`. - private boolean downloadWithExternalApp; - - // `currentDomainName` is used in `onCreate()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onAddDomain()`, and `applyDomainSettings()`. - private String currentDomainName; + private boolean downloadWithExternalApp; // TODO. // `orbotStatusBroadcastReceiver` is used in `onCreate()` and `onDestroy()`. private BroadcastReceiver orbotStatusBroadcastReceiver; @@ -353,22 +299,22 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook private boolean waitingForOrbot; // `domainSettingsJavaScriptEnabled` is used in `onOptionsItemSelected()` and `applyDomainSettings()`. - private Boolean domainSettingsJavaScriptEnabled; + private Boolean domainSettingsJavaScriptEnabled; // TODO. // `waitingForOrbotHtmlString` is used in `onCreate()` and `applyProxyThroughOrbot()`. - private String waitingForOrbotHtmlString; + private String waitingForOrbotHtmlString; // TODO. // `privateDataDirectoryString` is used in `onCreate()`, `onOptionsItemSelected()`, and `onNavigationItemSelected()`. - private String privateDataDirectoryString; + private String privateDataDirectoryString; // TODO. // `findOnPageEditText` is used in `onCreate()`, `onOptionsItemSelected()`, and `closeFindOnPage()`. - private EditText findOnPageEditText; + private EditText findOnPageEditText; // TODO. // `displayAdditionalAppBarIcons` is used in `onCreate()` and `onCreateOptionsMenu()`. - private boolean displayAdditionalAppBarIcons; + private boolean displayAdditionalAppBarIcons; // TODO. // The action bar drawer toggle is initialized in `onCreate()` and used in `onResume()`. - private ActionBarDrawerToggle actionBarDrawerToggle; + private ActionBarDrawerToggle actionBarDrawerToggle; // TODO. // The color spans are used in `onCreate()` and `highlightUrlText()`. private ForegroundColorSpan redColorSpan; @@ -381,23 +327,23 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook private int drawerHeaderPaddingBottom; // `sslErrorHandler` is used in `onCreate()`, `onSslErrorCancel()`, and `onSslErrorProceed`. - private SslErrorHandler sslErrorHandler; + private SslErrorHandler sslErrorHandler; // TODO. // `httpAuthHandler` is used in `onCreate()`, `onHttpAuthenticationCancel()`, and `onHttpAuthenticationProceed()`. - private static HttpAuthHandler httpAuthHandler; + private static HttpAuthHandler httpAuthHandler; // TODO. // `inputMethodManager` is used in `onOptionsItemSelected()`, `loadUrlFromTextBox()`, and `closeFindOnPage()`. - private InputMethodManager inputMethodManager; + private InputMethodManager inputMethodManager; // TODO. // `bookmarksDatabaseHelper` is used in `onCreate()`, `onDestroy`, `onOptionsItemSelected()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, // and `loadBookmarksFolder()`. - private BookmarksDatabaseHelper bookmarksDatabaseHelper; + private BookmarksDatabaseHelper bookmarksDatabaseHelper; // TODO. // `bookmarksListView` is used in `onCreate()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, and `loadBookmarksFolder()`. - private ListView bookmarksListView; + private ListView bookmarksListView; // TODO. // `bookmarksTitleTextView` is used in `onCreate()` and `loadBookmarksFolder()`. - private TextView bookmarksTitleTextView; + private TextView bookmarksTitleTextView; // TODO. // `bookmarksCursor` is used in `onDestroy()`, `onOptionsItemSelected()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and `loadBookmarksFolder()`. private Cursor bookmarksCursor; @@ -428,11 +374,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook private final int DOWNLOAD_IMAGE_REQUEST_CODE = 2; @Override - // Remove Android Studio's warning about the dangers of using SetJavaScriptEnabled. The whole premise of Privacy Browser is built around an understanding of these dangers. - // Also, remove the warning about needing to override `performClick()` when using an `OnTouchListener` with `WebView`. - @SuppressLint({"SetJavaScriptEnabled", "ClickableViewAccessibility"}) - // Remove Android Studio's warning about deprecations. The deprecated `getColor()` must be used until API >= 23. - @SuppressWarnings("deprecation") + // Remove the warning about needing to override `performClick()` when using an `OnTouchListener` with `WebView`. + @SuppressLint("ClickableViewAccessibility") protected void onCreate(Bundle savedInstanceState) { // Get a handle for the shared preferences. SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); @@ -479,7 +422,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook initialGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500)); finalGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500)); - // Get a handle for `urlTextBox`. + // Get handles for the URL views. EditText urlEditText = findViewById(R.id.url_edittext); // Remove the formatting from `urlTextBar` when the user is editing the text. @@ -516,8 +459,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Set `waitingForOrbotHTMLString`. waitingForOrbotHtmlString = "

" + getString(R.string.waiting_for_orbot) + "

"; - // Initialize `currentDomainName`, `orbotStatus`, and `waitingForOrbot`. - currentDomainName = ""; + // Initialize the Orbot status and the waiting for Orbot trackers. orbotStatus = "unknown"; waitingForOrbot = false; @@ -561,7 +503,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get handles for views that need to be modified. DrawerLayout drawerLayout = findViewById(R.id.drawerlayout); - final NavigationView navigationView = findViewById(R.id.navigationview); + NavigationView navigationView = findViewById(R.id.navigationview); TabLayout tabLayout = findViewById(R.id.tablayout); SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout); ViewPager webViewPager = findViewById(R.id.webviewpager); @@ -571,18 +513,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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); - fullScreenVideoFrameLayout = findViewById(R.id.full_screen_video_framelayout); // Listen for touches on the navigation menu. navigationView.setNavigationItemSelectedListener(this); // Get handles for the navigation menu and the back and forward menu items. The menu is zero-based. - final Menu navigationMenu = navigationView.getMenu(); - final MenuItem navigationCloseTabMenuItem = navigationMenu.getItem(0); - final MenuItem navigationBackMenuItem = navigationMenu.getItem(3); - final MenuItem navigationForwardMenuItem = navigationMenu.getItem(4); - final MenuItem navigationHistoryMenuItem = navigationMenu.getItem(5); - navigationRequestsMenuItem = navigationMenu.getItem(6); + Menu navigationMenu = navigationView.getMenu(); + MenuItem navigationCloseTabMenuItem = navigationMenu.getItem(0); + MenuItem navigationBackMenuItem = navigationMenu.getItem(3); + MenuItem navigationForwardMenuItem = navigationMenu.getItem(4); + MenuItem navigationHistoryMenuItem = navigationMenu.getItem(5); + MenuItem navigationRequestsMenuItem = navigationMenu.getItem(6); // Initialize the web view pager adapter. webViewPagerAdapter = new WebViewPagerAdapter(getSupportFragmentManager()); @@ -602,44 +543,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook @Override public void onPageSelected(int position) { - // Get the WebView tab fragment. - WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(position); - - // Get the fragment view. - View fragmentView = webViewTabFragment.getView(); - - // Remove the incorrect lint warning below that the fragment view might be null. - assert fragmentView != null; - - // Store the current WebView. - currentWebView = fragmentView.findViewById(R.id.nestedscroll_webview); - - // Store the current formatted URL string. - formattedUrlString = currentWebView.getUrl(); - - // Clear the focus from the URL text box. - urlEditText.clearFocus(); - - // Hide the soft keyboard. - inputMethodManager.hideSoftInputFromWindow(currentWebView.getWindowToken(), 0); - - // Display the current URL in the URL text box. - urlEditText.setText(formattedUrlString); - - // Highlight the URL text. - highlightUrlText(); - - // Set the background to indicate the domain settings status. - if (currentWebView.getDomainSettingsApplied()) { - // Set a green background on `urlTextBox` to indicate that custom domain settings are being used. The deprecated `.getDrawable()` must be used until the minimum API >= 21. - if (darkTheme) { - urlEditText.setBackground(getResources().getDrawable(R.drawable.url_bar_background_dark_blue)); - } else { - urlEditText.setBackground(getResources().getDrawable(R.drawable.url_bar_background_light_green)); - } - } else { - urlEditText.setBackgroundDrawable(getResources().getDrawable(R.color.transparent)); - } + // 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. if (tabLayout.getSelectedTabPosition() != position) { @@ -676,15 +581,15 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook @Override public void onTabReselected(TabLayout.Tab tab) { // Instantiate the View SSL Certificate dialog. - DialogFragment viewSslCertificateDialogFragment = ViewSslCertificateDialog.displayDialog(currentWebView.getWebViewFragmentId()); + DialogFragment viewSslCertificateDialogFragment = ViewSslCertificateDialog.displayDialog(currentWebView.getWebViewFragmentId(), currentWebView.getFavoriteOrDefaultIcon()); // Display the View SSL Certificate dialog. viewSslCertificateDialogFragment.show(getSupportFragmentManager(), getString(R.string.view_ssl_certificate)); } }); - // Add the first tab. (It doesn't matter what view is passed. That is just required as part of the ImageView `onClick()` syntax). - addTab(webViewPager); + // Add the first tab. + addTab(null); // Set the bookmarks drawer resources according to the theme. This can't be done in the layout due to compatibility issues with the `DrawerLayout` support widget. // The deprecated `getResources().getDrawable()` must be used until the minimum API >= 21 and and `getResources().getColor()` must be used until the minimum API >= 23. @@ -702,15 +607,28 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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. + // 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(); + + // Create a favorite icon byte array output stream. + ByteArrayOutputStream favoriteIconByteArrayOutputStream = new ByteArrayOutputStream(); + + // Convert the favorite icon bitmap to a byte array. `0` is for lossless compression (the only option for a PNG). + favoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, favoriteIconByteArrayOutputStream); + + // Convert the favorite icon byte array stream to a byte array. + byte[] favoriteIconByteArray = favoriteIconByteArrayOutputStream.toByteArray(); + // Create an intent to launch the bookmarks activity. Intent bookmarksIntent = new Intent(getApplicationContext(), BookmarksActivity.class); - // Include the current folder with the `Intent`. - bookmarksIntent.putExtra("Current Folder", currentBookmarksFolder); + // Add the extra information to the intent. + bookmarksIntent.putExtra("current_folder", currentBookmarksFolder); + bookmarksIntent.putExtra("favorite_icon_byte_array", favoriteIconByteArray); // Make it so. startActivity(bookmarksIntent); @@ -718,15 +636,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Set the create new bookmark folder FAB to display an alert dialog. createBookmarkFolderFab.setOnClickListener(v -> { - // Show the create bookmark folder dialog and name the instance `@string/create_folder`. - DialogFragment createBookmarkFolderDialog = new CreateBookmarkFolderDialog(); + // Create a create bookmark folder dialog. + DialogFragment createBookmarkFolderDialog = CreateBookmarkFolderDialog.createBookmarkFolder(currentWebView.getFavoriteOrDefaultIcon()); + + // Show the create bookmark folder dialog. createBookmarkFolderDialog.show(getSupportFragmentManager(), getString(R.string.create_folder)); }); // Set the create new bookmark FAB to display an alert dialog. createBookmarkFab.setOnClickListener(view -> { // Instantiate the create bookmark dialog. - DialogFragment createBookmarkDialog = CreateBookmarkDialog.createBookmark(currentWebView.getUrl(), currentWebView.getTitle(), favoriteIconBitmap); + DialogFragment createBookmarkDialog = CreateBookmarkDialog.createBookmark(currentWebView.getUrl(), currentWebView.getTitle(), currentWebView.getFavoriteOrDefaultIcon()); // Display the create bookmark dialog. createBookmarkDialog.show(getSupportFragmentManager(), getString(R.string.create_bookmark)); @@ -831,11 +751,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook oldFolderNameString = bookmarksCursor.getString(bookmarksCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME)); // Show the edit bookmark folder `AlertDialog` and name the instance `@string/edit_folder`. - DialogFragment editBookmarkFolderDialog = EditBookmarkFolderDialog.folderDatabaseId(databaseId); + DialogFragment editBookmarkFolderDialog = EditBookmarkFolderDialog.folderDatabaseId(databaseId, currentWebView.getFavoriteOrDefaultIcon()); editBookmarkFolderDialog.show(getSupportFragmentManager(), getString(R.string.edit_folder)); } else { // Show the edit bookmark `AlertDialog` and name the instance `@string/edit_bookmark`. - DialogFragment editBookmarkDialog = EditBookmarkDialog.bookmarkDatabaseId(databaseId); + DialogFragment editBookmarkDialog = EditBookmarkDialog.bookmarkDatabaseId(databaseId, currentWebView.getFavoriteOrDefaultIcon()); editBookmarkDialog.show(getSupportFragmentManager(), getString(R.string.edit_bookmark)); } @@ -906,18 +826,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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); - // Initialize cookieManager. - cookieManager = CookieManager.getInstance(); - // Replace the header that `WebView` creates for `X-Requested-With` with a null value. The default value is the application ID (com.stoutner.privacybrowser.standard). customHeaders.put("X-Requested-With", ""); // Initialize the default preference values the first time the program is run. `false` keeps this command from resetting any current preferences back to default. PreferenceManager.setDefaultValues(this, R.xml.preferences, false); - // Get a handle for the `Runtime`. - privacyBrowserRuntime = Runtime.getRuntime(); - // Store the application's private data directory. privateDataDirectoryString = getApplicationInfo().dataDir; // `dataDir` will vary, but will be something like `/data/user/0/com.stoutner.privacybrowser.standard`, which links to `/data/data/com.stoutner.privacybrowser.standard`. @@ -926,10 +840,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook inFullScreenBrowsingMode = false; // Initialize the privacy settings variables. - javaScriptEnabled = false; firstPartyCookiesEnabled = false; - thirdPartyCookiesEnabled = false; - domStorageEnabled = false; saveFormDataEnabled = false; // Form data can be removed once the minimum API >= 26. nightMode = false; @@ -945,17 +856,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Destroy the bare WebView. bareWebView.destroy(); - // Initialize the favorite icon bitmap. `ContextCompat` must be used until API >= 21. - Drawable favoriteIconDrawable = ContextCompat.getDrawable(getApplicationContext(), R.drawable.world); - BitmapDrawable favoriteIconBitmapDrawable = (BitmapDrawable) favoriteIconDrawable; - assert favoriteIconBitmapDrawable != null; - favoriteIconDefaultBitmap = favoriteIconBitmapDrawable.getBitmap(); - - // If the favorite icon is null, load the default. - if (favoriteIconBitmap == null) { - favoriteIconBitmap = favoriteIconDefaultBitmap; - } - // 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); @@ -989,50 +889,56 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook @Override protected void onNewIntent(Intent intent) { - // Sets the new intent as the activity intent, so that any future `getIntent()`s pick up this one instead of creating a new activity. - setIntent(intent); - // Get the information from the intent. String intentAction = intent.getAction(); Uri intentUriData = intent.getData(); - // 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. - String encodedUrlString; + // Only process the URI if it contains data. If the user pressed the desktop icon after the app was already running the URI will be null. + if (intentUriData != null) { + // Sets the new intent as the activity intent, which replaces the one that originally started the app. + setIntent(intent); - // 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 a new tab. + addTab(null); + + // 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. + 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. + formattedUrlString = searchURL + encodedUrlString; + } else { // The intent should contain a URL. + // Set the formatted URL string. + formattedUrlString = intentUriData.toString(); } - // Add the base search URL. - formattedUrlString = searchURL + encodedUrlString; - } else if (intentUriData != null){ // Check to see if the intent contains a new URL. - // Set the formatted URL string. - formattedUrlString = intentUriData.toString(); - } + // Load the URL. + loadUrl(formattedUrlString); - // Load the URL. - loadUrl(formattedUrlString); + // Get a handle for the drawer layout. + DrawerLayout drawerLayout = findViewById(R.id.drawerlayout); - // 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); + } - // Close the navigation drawer if it is open. - if (drawerLayout.isDrawerVisible(GravityCompat.START)) { - drawerLayout.closeDrawer(GravityCompat.START); - } + // Close the bookmarks drawer if it is open. + if (drawerLayout.isDrawerVisible(GravityCompat.END)) { + drawerLayout.closeDrawer(GravityCompat.END); + } - // Close the bookmarks drawer if it is open. - if (drawerLayout.isDrawerVisible(GravityCompat.END)) { - drawerLayout.closeDrawer(GravityCompat.END); + // Clear the keyboard if displayed and remove the focus on the urlTextBar if it has it. + currentWebView.requestFocus(); } - - // Clear the keyboard if displayed and remove the focus on the urlTextBar if it has it. - currentWebView.requestFocus(); } @Override @@ -1229,8 +1135,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Inflate the menu. This adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.webview_options_menu, menu); - // Set mainMenu so it can be used by `onOptionsItemSelected()` and `updatePrivacyIcons`. - mainMenu = menu; + // 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. updatePrivacyIcons(false); @@ -1279,7 +1185,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } // Replace Refresh with Stop if a URL is already loading. - if (urlIsLoading) { + if (currentWebView != null && currentWebView.getProgress() != 100) { // Set the title. refreshMenuItem.setTitle(R.string.stop); @@ -1317,6 +1223,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook MenuItem nightModeMenuItem = menu.findItem(R.id.night_mode); MenuItem proxyThroughOrbotMenuItem = menu.findItem(R.id.proxy_through_orbot); + // Get a handle for the cookie manager. + CookieManager cookieManager = CookieManager.getInstance(); + // Initialize the current user agent string and the font size. String currentUserAgent = getString(R.string.user_agent_privacy_browser); int fontSize = 100; @@ -1336,40 +1245,44 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get the current font size from the fontSize = currentWebView.getSettings().getTextZoom(); - // Set the status of the display images menu item. + // Set the status of the menu item checkboxes. + toggleDomStorageMenuItem.setChecked(currentWebView.getSettings().getDomStorageEnabled()); + 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)); displayImagesMenuItem.setChecked(currentWebView.getSettings().getLoadsImagesAutomatically()); // Initialize the display names for the blocklists with the number of blocked requests. blocklistsMenuItem.setTitle(getString(R.string.blocklists) + " - " + currentWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS)); - easyListMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.EASY_LIST_BLOCKED_REQUESTS) + " - " + getString(R.string.easylist)); - easyPrivacyMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.EASY_PRIVACY_BLOCKED_REQUESTS) + " - " + getString(R.string.easyprivacy)); - fanboysAnnoyanceListMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST_BLOCKED_REQUESTS) + " - " + getString(R.string.fanboys_annoyance_list)); - fanboysSocialBlockingListMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST_BLOCKED_REQUESTS) + " - " + - getString(R.string.fanboys_social_blocking_list)); - ultraPrivacyMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.ULTRA_PRIVACY_BLOCKED_REQUESTS) + " - " + getString(R.string.ultraprivacy)); - blockAllThirdPartyRequestsMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.THIRD_PARTY_BLOCKED_REQUESTS) + " - " + getString(R.string.block_all_third_party_requests)); + easyListMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.EASY_LIST) + " - " + getString(R.string.easylist)); + easyPrivacyMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.EASY_PRIVACY) + " - " + getString(R.string.easyprivacy)); + fanboysAnnoyanceListMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST) + " - " + getString(R.string.fanboys_annoyance_list)); + 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); - toggleThirdPartyCookiesMenuItem.setChecked(thirdPartyCookiesEnabled); - toggleDomStorageMenuItem.setChecked(domStorageEnabled); toggleSaveFormDataMenuItem.setChecked(saveFormDataEnabled); // Form data can be removed once the minimum API >= 26. - easyListMenuItem.setChecked(easyListEnabled); - easyPrivacyMenuItem.setChecked(easyPrivacyEnabled); - fanboysAnnoyanceListMenuItem.setChecked(fanboysAnnoyanceListEnabled); - fanboysSocialBlockingListMenuItem.setChecked(fanboysSocialBlockingListEnabled); - ultraPrivacyMenuItem.setChecked(ultraPrivacyEnabled); - blockAllThirdPartyRequestsMenuItem.setChecked(blockAllThirdPartyRequests); swipeToRefreshMenuItem.setChecked(swipeRefreshLayout.isEnabled()); nightModeMenuItem.setChecked(nightMode); proxyThroughOrbotMenuItem.setChecked(proxyThroughOrbot); - // Enable third-party cookies if first-party cookies are enabled. - toggleThirdPartyCookiesMenuItem.setEnabled(firstPartyCookiesEnabled); + // 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. + toggleThirdPartyCookiesMenuItem.setEnabled(firstPartyCookiesEnabled); + } // Enable DOM Storage if JavaScript is enabled. - toggleDomStorageMenuItem.setEnabled(javaScriptEnabled); + toggleDomStorageMenuItem.setEnabled(currentWebView.getSettings().getJavaScriptEnabled()); // Enable Clear Cookies if there are any. clearCookiesMenuItem.setEnabled(cookieManager.hasCookies()); @@ -1403,8 +1316,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Enable Clear Data if any of the submenu items are enabled. clearDataMenuItem.setEnabled(clearCookiesMenuItem.isEnabled() || clearDOMStorageMenuItem.isEnabled() || clearFormDataMenuItem.isEnabled()); - // Disable Fanboy's Social Blocking List if Fanboy's Annoyance List is checked. - fanboysSocialBlockingListMenuItem.setEnabled(!fanboysAnnoyanceListEnabled); + // Disable Fanboy's Social Blocking List menu item if Fanboy's Annoyance List is checked. + fanboysSocialBlockingListMenuItem.setEnabled(!fanboysAnnoyanceListMenuItem.isChecked()); // Select the current user agent menu item. A switch statement cannot be used because the user agents are not compile time constants. if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[0])) { // Privacy Browser. @@ -1524,20 +1437,23 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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(); + // Run the commands that correlate to the selected menu item. switch (menuItemId) { case R.id.toggle_javascript: - // Switch the status of javaScriptEnabled. - javaScriptEnabled = !javaScriptEnabled; - - // Apply the new JavaScript status. - currentWebView.getSettings().setJavaScriptEnabled(javaScriptEnabled); + // 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 (javaScriptEnabled) { // JavaScrip is enabled. + if (currentWebView.getSettings().getJavaScriptEnabled()) { // JavaScrip is enabled. Snackbar.make(findViewById(R.id.webviewpager), R.string.javascript_enabled, Snackbar.LENGTH_SHORT).show(); } else if (firstPartyCookiesEnabled) { // JavaScript is disabled, but first-party cookies are enabled. Snackbar.make(findViewById(R.id.webviewpager), R.string.javascript_disabled, Snackbar.LENGTH_SHORT).show(); @@ -1553,7 +1469,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook if (currentWebView.getDomainSettingsApplied()) { // Edit the current domain settings. // Reapply the domain settings on returning to `MainWebViewActivity`. reapplyDomainSettingsOnRestart = true; - currentDomainName = ""; + 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. @@ -1564,15 +1480,15 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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. - domainsIntent.putExtra("loadDomain", currentWebView.getDomainSettingsDatabaseId()); - domainsIntent.putExtra("closeOnBack", true); + domainsIntent.putExtra("load_domain", currentWebView.getDomainSettingsDatabaseId()); + domainsIntent.putExtra("close_on_back", true); // Make it so. startActivity(domainsIntent); } else { // Add a new domain. // Apply the new domain settings on returning to `MainWebViewActivity`. reapplyDomainSettingsOnRestart = true; - currentDomainName = ""; + currentWebView.resetCurrentDomainName(); // Get the current domain Uri currentUri = Uri.parse(formattedUrlString); @@ -1593,8 +1509,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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. - domainsIntent.putExtra("loadDomain", newDomainDatabaseId); - domainsIntent.putExtra("closeOnBack", true); + domainsIntent.putExtra("load_domain", newDomainDatabaseId); + domainsIntent.putExtra("close_on_back", true); // Make it so. startActivity(domainsIntent); @@ -1617,7 +1533,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Display a `Snackbar`. if (firstPartyCookiesEnabled) { // First-party cookies are enabled. Snackbar.make(findViewById(R.id.webviewpager), R.string.first_party_cookies_enabled, Snackbar.LENGTH_SHORT).show(); - } else if (javaScriptEnabled) { // JavaScript is still enabled. + } 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(); @@ -1630,16 +1546,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook case R.id.toggle_third_party_cookies: if (Build.VERSION.SDK_INT >= 21) { // Switch the status of thirdPartyCookiesEnabled. - thirdPartyCookiesEnabled = !thirdPartyCookiesEnabled; + cookieManager.setAcceptThirdPartyCookies(currentWebView, !cookieManager.acceptThirdPartyCookies(currentWebView)); // Update the menu checkbox. - menuItem.setChecked(thirdPartyCookiesEnabled); - - // Apply the new cookie status. - cookieManager.setAcceptThirdPartyCookies(currentWebView, thirdPartyCookiesEnabled); + menuItem.setChecked(cookieManager.acceptThirdPartyCookies(currentWebView)); - // Display a `Snackbar`. - if (thirdPartyCookiesEnabled) { + // 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(); @@ -1651,20 +1564,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook return true; case R.id.toggle_dom_storage: - // Switch the status of domStorageEnabled. - domStorageEnabled = !domStorageEnabled; + // Toggle the status of domStorageEnabled. + currentWebView.getSettings().setDomStorageEnabled(!currentWebView.getSettings().getDomStorageEnabled()); // Update the menu checkbox. - menuItem.setChecked(domStorageEnabled); - - // Apply the new DOM Storage status. - currentWebView.getSettings().setDomStorageEnabled(domStorageEnabled); + menuItem.setChecked(currentWebView.getSettings().getDomStorageEnabled()); - // Update the privacy icon. `true` runs `invalidateOptionsMenu` as the last step. + // Update the privacy icon. `true` refreshes the app bar icons. updatePrivacyIcons(true); - // Display a `Snackbar`. - if (domStorageEnabled) { + // 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(); @@ -1755,14 +1665,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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(); + // A string array must be used because the directory contains a space and `Runtime.exec` will otherwise not escape the string correctly. - Process deleteLocalStorageProcess = privacyBrowserRuntime.exec(new String[]{"rm", "-rf", privateDataDirectoryString + "/app_webview/Local Storage/"}); + 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 = privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/IndexedDB"); - Process deleteQuotaManagerProcess = privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager"); - Process deleteQuotaManagerJournalProcess = privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager-journal"); - Process deleteDatabasesProcess = privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/databases"); + 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(); @@ -1812,10 +1725,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook case R.id.easylist: // Toggle the EasyList status. - easyListEnabled = !easyListEnabled; + currentWebView.enableBlocklist(NestedScrollWebView.EASY_LIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.EASY_LIST)); // Update the menu checkbox. - menuItem.setChecked(easyListEnabled); + menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASY_LIST)); // Reload the current WebView. currentWebView.reload(); @@ -1823,10 +1736,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook case R.id.easyprivacy: // Toggle the EasyPrivacy status. - easyPrivacyEnabled = !easyPrivacyEnabled; + currentWebView.enableBlocklist(NestedScrollWebView.EASY_PRIVACY, !currentWebView.isBlocklistEnabled(NestedScrollWebView.EASY_PRIVACY)); // Update the menu checkbox. - menuItem.setChecked(easyPrivacyEnabled); + menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASY_PRIVACY)); // Reload the current WebView. currentWebView.reload(); @@ -1834,14 +1747,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook case R.id.fanboys_annoyance_list: // Toggle Fanboy's Annoyance List status. - fanboysAnnoyanceListEnabled = !fanboysAnnoyanceListEnabled; + currentWebView.enableBlocklist(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST)); // Update the menu checkbox. - menuItem.setChecked(fanboysAnnoyanceListEnabled); + menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST)); // Update the staus of Fanboy's Social Blocking List. - MenuItem fanboysSocialBlockingListMenuItem = mainMenu.findItem(R.id.fanboys_social_blocking_list); - fanboysSocialBlockingListMenuItem.setEnabled(!fanboysAnnoyanceListEnabled); + MenuItem fanboysSocialBlockingListMenuItem = optionsMenu.findItem(R.id.fanboys_social_blocking_list); + fanboysSocialBlockingListMenuItem.setEnabled(!currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST)); // Reload the current WebView. currentWebView.reload(); @@ -1849,10 +1762,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook case R.id.fanboys_social_blocking_list: // Toggle Fanboy's Social Blocking List status. - fanboysSocialBlockingListEnabled = !fanboysSocialBlockingListEnabled; + currentWebView.enableBlocklist(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST)); // Update the menu checkbox. - menuItem.setChecked(fanboysSocialBlockingListEnabled); + menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST)); // Reload the current WebView. currentWebView.reload(); @@ -1860,10 +1773,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook case R.id.ultraprivacy: // Toggle the UltraPrivacy status. - ultraPrivacyEnabled = !ultraPrivacyEnabled; + currentWebView.enableBlocklist(NestedScrollWebView.ULTRA_PRIVACY, !currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRA_PRIVACY)); // Update the menu checkbox. - menuItem.setChecked(ultraPrivacyEnabled); + menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRA_PRIVACY)); // Reload the current WebView. currentWebView.reload(); @@ -1871,10 +1784,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook case R.id.block_all_third_party_requests: //Toggle the third-party requests blocker status. - blockAllThirdPartyRequests = !blockAllThirdPartyRequests; + currentWebView.enableBlocklist(NestedScrollWebView.THIRD_PARTY_REQUESTS, !currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS)); // Update the menu checkbox. - menuItem.setChecked(blockAllThirdPartyRequests); + menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS)); // Reload the current WebView. currentWebView.reload(); @@ -1978,7 +1891,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook case R.id.user_agent_custom: // Update the user agent. - currentWebView.getSettings().setUserAgentString(defaultCustomUserAgentString); + currentWebView.getSettings().setUserAgentString(sharedPreferences.getString("custom_user_agent", getString(R.string.custom_user_agent_default_value))); // Reload the current WebView. currentWebView.reload(); @@ -2042,23 +1955,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook nightMode = !nightMode; // Enable or disable JavaScript according to night mode, the global preference, and any domain settings. - if (nightMode) { // Night mode is enabled. Enable JavaScript. - // Update the global variable. - javaScriptEnabled = true; + if (nightMode) { // 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. - // Get the JavaScript preference that was stored the last time domain settings were loaded. - javaScriptEnabled = domainSettingsJavaScriptEnabled; + // Apply the JavaScript preference that was stored the last time domain settings were loaded. + currentWebView.getSettings().setJavaScriptEnabled(domainSettingsJavaScriptEnabled); } else { // Night mode is disabled and domain settings are not applied. Set JavaScript according to the global preference. - // Get a handle for the shared preference. - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); - - // Get the JavaScript preference. - javaScriptEnabled = sharedPreferences.getBoolean("javascript", false); + // Apply the JavaScript preference. + currentWebView.getSettings().setJavaScriptEnabled(sharedPreferences.getBoolean("javascript", false)); } - // Apply the JavaScript setting to the WebView. - currentWebView.getSettings().setJavaScriptEnabled(javaScriptEnabled); - // Update the privacy icons. updatePrivacyIcons(false); @@ -2089,8 +1996,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook return true; case R.id.view_source: - // Launch the View Source activity. + // 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. + viewSourceIntent.putExtra("user_agent", currentWebView.getSettings().getUserAgentString()); + + // Make it so. startActivity(viewSourceIntent); return true; @@ -2131,7 +2043,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook case R.id.add_to_homescreen: // Instantiate the create home screen shortcut dialog. - DialogFragment createHomeScreenShortcutDialogFragment = CreateHomeScreenShortcutDialog.createDialog(currentWebView.getTitle(), formattedUrlString, favoriteIconBitmap); + DialogFragment createHomeScreenShortcutDialogFragment = CreateHomeScreenShortcutDialog.createDialog(currentWebView.getTitle(), formattedUrlString, currentWebView.getFavoriteOrDefaultIcon()); // Show the create home screen shortcut dialog. createHomeScreenShortcutDialogFragment.show(getSupportFragmentManager(), getString(R.string.create_shortcut)); @@ -2177,8 +2089,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Run the commands that correspond to the selected menu item. switch (menuItemId) { case R.id.close_tab: - // Get a handle for the tab layout. + // 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 current tab number. int currentTabNumber = tabLayout.getSelectedTabPosition(); @@ -2186,8 +2099,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Delete the current tab. tabLayout.removeTabAt(currentTabNumber); - // Delete the current page. - webViewPagerAdapter.deletePage(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. + if (webViewPagerAdapter.deletePage(currentTabNumber, webViewPager)) { + setCurrentWebView(currentTabNumber); + } break; case R.id.clear_and_exit: @@ -2201,20 +2116,23 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get the status of the clear everything preference. boolean clearEverything = sharedPreferences.getBoolean("clear_everything", true); + // Get a handle for the runtime. + Runtime runtime = Runtime.getRuntime(); + // Clear cookies. if (clearEverything || sharedPreferences.getBoolean("clear_cookies", true)) { // The command to remove cookies changed slightly in API 21. if (Build.VERSION.SDK_INT >= 21) { - cookieManager.removeAllCookies(null); + CookieManager.getInstance().removeAllCookies(null); } else { - cookieManager.removeAllCookie(); + CookieManager.getInstance().removeAllCookie(); } // Manually delete the cookies database, as `CookieManager` sometimes will not flush its changes to disk before `System.exit(0)` is run. try { // Two commands must be used because `Runtime.exec()` does not like `*`. - Process deleteCookiesProcess = privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/Cookies"); - Process deleteCookiesJournalProcess = privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/Cookies-journal"); + Process deleteCookiesProcess = runtime.exec("rm -f " + privateDataDirectoryString + "/app_webview/Cookies"); + Process deleteCookiesJournalProcess = runtime.exec("rm -f " + privateDataDirectoryString + "/app_webview/Cookies-journal"); // Wait until the processes have finished. deleteCookiesProcess.waitFor(); @@ -2233,13 +2151,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Manually delete the DOM storage files and directories, as `WebStorage` sometimes will not flush its changes to disk before `System.exit(0)` is run. try { // A `String[]` must be used because the directory contains a space and `Runtime.exec` will otherwise not escape the string correctly. - Process deleteLocalStorageProcess = privacyBrowserRuntime.exec(new String[] {"rm", "-rf", privateDataDirectoryString + "/app_webview/Local Storage/"}); + 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 = privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/IndexedDB"); - Process deleteQuotaManagerProcess = privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager"); - Process deleteQuotaManagerJournalProcess = privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager-journal"); - Process deleteDatabaseProcess = privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/databases"); + 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 deleteDatabaseProcess = runtime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/databases"); // Wait until the processes have finished. deleteLocalStorageProcess.waitFor(); @@ -2260,8 +2178,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Manually delete the form data database, as `WebViewDatabase` sometimes will not flush its changes to disk before `System.exit(0)` is run. try { // A string array must be used because the database contains a space and `Runtime.exec` will not otherwise escape the string correctly. - Process deleteWebDataProcess = privacyBrowserRuntime.exec(new String[] {"rm", "-f", privateDataDirectoryString + "/app_webview/Web Data"}); - Process deleteWebDataJournalProcess = privacyBrowserRuntime.exec(new String[] {"rm", "-f", privateDataDirectoryString + "/app_webview/Web Data-journal"}); + Process deleteWebDataProcess = runtime.exec(new String[] {"rm", "-f", privateDataDirectoryString + "/app_webview/Web Data"}); + Process deleteWebDataJournalProcess = runtime.exec(new String[] {"rm", "-f", privateDataDirectoryString + "/app_webview/Web Data-journal"}); // Wait until the processes have finished. deleteWebDataProcess.waitFor(); @@ -2294,11 +2212,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Manually delete the cache directories. try { // Delete the main cache directory. - Process deleteCacheProcess = privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/cache"); + Process deleteCacheProcess = runtime.exec("rm -rf " + privateDataDirectoryString + "/cache"); // Delete the secondary `Service Worker` cache directory. // A string array must be used because the directory contains a space and `Runtime.exec` will otherwise not escape the string correctly. - Process deleteServiceWorkerProcess = privacyBrowserRuntime.exec(new String[] {"rm", "-rf", privateDataDirectoryString + "/app_webview/Service Worker/"}); + Process deleteServiceWorkerProcess = runtime.exec(new String[] {"rm", "-rf", privateDataDirectoryString + "/app_webview/Service Worker/"}); // Wait until the processes have finished. deleteCacheProcess.waitFor(); @@ -2343,7 +2261,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook if (clearEverything) { try { // Delete the folder. - Process deleteAppWebviewProcess = privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview"); + Process deleteAppWebviewProcess = runtime.exec("rm -rf " + privateDataDirectoryString + "/app_webview"); // Wait until the process has finished. deleteAppWebviewProcess.waitFor(); @@ -2409,6 +2327,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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; @@ -2426,7 +2347,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook case R.id.domains: // Set the flag to reapply the domain settings on restart when returning from Domain Settings. reapplyDomainSettingsOnRestart = true; - currentDomainName = ""; + 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. @@ -2444,7 +2365,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Set the flag to reapply the domain settings on restart when returning from Settings. reapplyDomainSettingsOnRestart = true; - currentDomainName = ""; + currentWebView.resetCurrentDomainName(); // TODO. Do this for all tabs. // Launch the settings activity. Intent settingsIntent = new Intent(this, SettingsActivity.class); @@ -2800,7 +2721,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } @Override - public void onCreateBookmark(DialogFragment dialogFragment) { + public void onCreateBookmark(DialogFragment dialogFragment, Bitmap favoriteIconBitmap) { // 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); @@ -2809,19 +2730,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook String bookmarkNameString = createBookmarkNameEditText.getText().toString(); String bookmarkUrlString = createBookmarkUrlEditText.getText().toString(); - // Get a copy of the favorite icon bitmap. - Bitmap favoriteIcon = favoriteIconBitmap; - - // Scale the favorite icon bitmap down if it is larger than 256 x 256. Filtering uses bilinear interpolation. - if ((favoriteIcon.getHeight() > 256) || (favoriteIcon.getWidth() > 256)) { - favoriteIcon = Bitmap.createScaledBitmap(favoriteIcon, 256, 256, true); - } - // Create a favorite icon byte array output stream. ByteArrayOutputStream favoriteIconByteArrayOutputStream = new ByteArrayOutputStream(); // Convert the favorite icon bitmap to a byte array. `0` is for lossless compression (the only option for a PNG). - favoriteIcon.compress(Bitmap.CompressFormat.PNG, 0, favoriteIconByteArrayOutputStream); + favoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, favoriteIconByteArrayOutputStream); // Convert the favorite icon byte array stream to a byte array. byte[] favoriteIconByteArray = favoriteIconByteArrayOutputStream.toByteArray(); @@ -2843,7 +2756,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } @Override - public void onCreateBookmarkFolder(DialogFragment dialogFragment) { + public void onCreateBookmarkFolder(DialogFragment dialogFragment, Bitmap favoriteIconBitmap) { // 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); @@ -2866,13 +2779,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Convert the folder icon bitmap drawable to a bitmap. folderIconBitmap = folderIconBitmapDrawable.getBitmap(); } else { // Use the WebView favorite icon. - // Get a copy of the favorite icon bitmap. + // Copy the favorite icon bitmap to the folder icon bitmap. folderIconBitmap = favoriteIconBitmap; - - // Scale the folder icon bitmap down if it is larger than 256 x 256. Filtering uses bilinear interpolation. - if ((folderIconBitmap.getHeight() > 256) || (folderIconBitmap.getWidth() > 256)) { - folderIconBitmap = Bitmap.createScaledBitmap(folderIconBitmap, 256, 256, true); - } } // Create a folder icon byte array output stream. @@ -2904,7 +2812,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } @Override - public void onSaveBookmark(DialogFragment dialogFragment, int selectedBookmarkDatabaseId) { + public void onSaveBookmark(DialogFragment dialogFragment, int selectedBookmarkDatabaseId, Bitmap favoriteIconBitmap) { // Get handles for the views from `dialogFragment`. EditText editBookmarkNameEditText = dialogFragment.getDialog().findViewById(R.id.edit_bookmark_name_edittext); EditText editBookmarkUrlEditText = dialogFragment.getDialog().findViewById(R.id.edit_bookmark_url_edittext); @@ -2918,19 +2826,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook if (currentBookmarkIconRadioButton.isChecked()) { // Update the bookmark without changing the favorite icon. bookmarksDatabaseHelper.updateBookmark(selectedBookmarkDatabaseId, bookmarkNameString, bookmarkUrlString); } else { // Update the bookmark using the `WebView` favorite icon. - // Get a copy of the favorite icon bitmap. - Bitmap favoriteIcon = favoriteIconBitmap; - - // Scale the favorite icon bitmap down if it is larger than 256 x 256. Filtering uses bilinear interpolation. - if ((favoriteIcon.getHeight() > 256) || (favoriteIcon.getWidth() > 256)) { - favoriteIcon = Bitmap.createScaledBitmap(favoriteIcon, 256, 256, true); - } - // Create a favorite icon byte array output stream. ByteArrayOutputStream newFavoriteIconByteArrayOutputStream = new ByteArrayOutputStream(); // Convert the favorite icon bitmap to a byte array. `0` is for lossless compression (the only option for a PNG). - favoriteIcon.compress(Bitmap.CompressFormat.PNG, 0, newFavoriteIconByteArrayOutputStream); + favoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, newFavoriteIconByteArrayOutputStream); // Convert the favorite icon byte array stream to a byte array. byte[] newFavoriteIconByteArray = newFavoriteIconByteArrayOutputStream.toByteArray(); @@ -2947,7 +2847,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } @Override - public void onSaveBookmarkFolder(DialogFragment dialogFragment, int selectedFolderDatabaseId) { + public void onSaveBookmarkFolder(DialogFragment dialogFragment, int selectedFolderDatabaseId, Bitmap favoriteIconBitmap) { // Get handles for the views from `dialogFragment`. EditText editFolderNameEditText = dialogFragment.getDialog().findViewById(R.id.edit_folder_name_edittext); RadioButton currentFolderIconRadioButton = dialogFragment.getDialog().findViewById(R.id.edit_folder_current_icon_radiobutton); @@ -2976,13 +2876,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Convert the folder icon bitmap drawable to a bitmap. folderIconBitmap = folderIconBitmapDrawable.getBitmap(); } else { // Use the `WebView` favorite icon. - // Get a copy of the favorite icon bitmap. - folderIconBitmap = MainWebViewActivity.favoriteIconBitmap; - - // Scale the folder icon bitmap down if it is larger than 256 x 256. Filtering uses bilinear interpolation. - if ((folderIconBitmap.getHeight() > 256) || (folderIconBitmap.getWidth() > 256)) { - folderIconBitmap = Bitmap.createScaledBitmap(folderIconBitmap, 256, 256, true); - } + // Copy the favorite icon bitmap to the folder icon bitmap. + folderIconBitmap = favoriteIconBitmap; } // Create a folder icon byte array output stream. @@ -3009,13 +2904,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Convert the folder icon bitmap drawable to a bitmap. folderIconBitmap = folderIconBitmapDrawable.getBitmap(); } else { // Use the `WebView` favorite icon. - // Get a copy of the favorite icon bitmap. - folderIconBitmap = MainWebViewActivity.favoriteIconBitmap; - - // Scale the folder icon bitmap down if it is larger than 256 x 256. Filtering uses bilinear interpolation. - if ((folderIconBitmap.getHeight() > 256) || (folderIconBitmap.getWidth() > 256)) { - folderIconBitmap = Bitmap.createScaledBitmap(folderIconBitmap, 256, 256, true); - } + // Copy the favorite icon bitmap to the folder icon bitmap. + folderIconBitmap = favoriteIconBitmap; } // Create a folder icon byte array output stream. @@ -3107,7 +2997,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Code contributed 2017 Hendrik Knackstedt. Copyright assigned to Soren Stoutner . if (firstPartyCookiesEnabled) { // Get the cookies for `imageUrl`. - String cookies = cookieManager.getCookie(imageUrl); + String cookies = CookieManager.getInstance().getCookie(imageUrl); // Add the cookies to `downloadRequest`. In the HTTP request header, cookies are named `Cookie`. downloadRequest.addRequestHeader("Cookie", cookies); @@ -3159,7 +3049,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Code contributed 2017 Hendrik Knackstedt. Copyright assigned to Soren Stoutner . if (firstPartyCookiesEnabled) { // Get the cookies for `downloadUrl`. - String cookies = cookieManager.getCookie(downloadUrl); + String cookies = CookieManager.getInstance().getCookie(downloadUrl); // Add the cookies to `downloadRequest`. In the HTTP request header, cookies are named `Cookie`. downloadRequest.addRequestHeader("Cookie", cookies); @@ -3214,12 +3104,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } @Override - public void onSslErrorCancel() { + public void onSslErrorCancel() { // TODO. How to handle this with multiple tabs? There could be multiple errors at once. sslErrorHandler.cancel(); } @Override - public void onSslErrorProceed() { + public void onSslErrorProceed() { // TODO. How to handle this with multiple tabs? There could be multiple errors at once. sslErrorHandler.proceed(); } @@ -3388,9 +3278,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Apply the domain settings. applyDomainSettings(currentWebView, url, true, false); - // If loading a website, set `urlIsLoading` to prevent changes in the user agent on websites with redirects from reloading the current website. - urlIsLoading = !url.equals(""); - // Load the URL. currentWebView.loadUrl(url, customHeaders); } @@ -3521,45 +3408,29 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `reloadWebsite` is used if returning from the Domains activity. Otherwise JavaScript might not function correctly if it is newly enabled. - // The deprecated `.getDrawable()` must be used until the minimum API >= 21. - @SuppressWarnings("deprecation") + @SuppressLint("SetJavaScriptEnabled") private boolean applyDomainSettings(NestedScrollWebView nestedScrollWebView, String url, boolean resetFavoriteIcon, boolean reloadWebsite) { - // Get a handle for the URL edit text. - EditText urlEditText = findViewById(R.id.url_edittext); + // Get a handle for the URL relative layout. + RelativeLayout urlRelativeLayout = findViewById(R.id.url_relativelayout); - // Get the current user agent. + // Store a copy of the current user agent to track changes for the return boolean. String initialUserAgent = nestedScrollWebView.getSettings().getUserAgentString(); - // Initialize a variable to track if the user agent changes. - boolean userAgentChanged = false; - // Parse the URL into a URI. Uri uri = Uri.parse(url); // Extract the domain from `uri`. - String hostName = uri.getHost(); - - // Initialize `loadingNewDomainName`. - boolean loadingNewDomainName; - - // If either `hostName` or `currentDomainName` are `null`, run the options for loading a new domain name. - // The lint suggestion to simplify the `if` statement is incorrect, because `hostName.equals(currentDomainName)` can produce a `null object reference.` - //noinspection SimplifiableIfStatement - if ((hostName == null) || (currentDomainName == null)) { // TODO. - loadingNewDomainName = true; - } else { // Determine if `hostName` equals `currentDomainName`. - loadingNewDomainName = !hostName.equals(currentDomainName); // TODO. - } + String newHostName = uri.getHost(); // Strings don't like to be null. - if (hostName == null) { - hostName = ""; + if (newHostName == null) { + newHostName = ""; } // Only apply the domain settings if a new domain is being loaded. This allows the user to set temporary settings for JavaScript, cookies, DOM storage, etc. - if (loadingNewDomainName) { - // Set the new `hostname` as the `currentDomainName`. - currentDomainName = hostName; // TODO. + if (!nestedScrollWebView.getCurrentDomainName().equals(newHostName)) { + // Set the new host name as the current domain name. + nestedScrollWebView.setCurrentDomainName(newHostName); // Reset the ignoring of pinned domain information. nestedScrollWebView.setIgnorePinnedDomainInformation(false); @@ -3570,8 +3441,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Reset the favorite icon if specified. if (resetFavoriteIcon) { - // Store the favorite icon bitmap. - favoriteIconBitmap = favoriteIconDefaultBitmap; // TODO. + // Initialize the favorite icon. + currentWebView.initializeFavoriteIcon(); // Get a handle for the tab layout. TabLayout tabLayout = findViewById(R.id.tablayout); @@ -3592,7 +3463,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook ImageView currentTabFavoriteIconImageView = currentTabCustomView.findViewById(R.id.favorite_icon_imageview); // Set the default favorite icon as the favorite icon for this tab. - currentTabFavoriteIconImageView.setImageBitmap(Bitmap.createScaledBitmap(favoriteIconBitmap, 64, 64, true)); + currentTabFavoriteIconImageView.setImageBitmap(Bitmap.createScaledBitmap(nestedScrollWebView.getFavoriteOrDefaultIcon(), 64, 64, true)); } // Get a handle for the swipe refresh layout. @@ -3626,9 +3497,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook String domainNameInDatabase = null; // Check the hostname against the domain settings set. - if (domainSettingsSet.contains(hostName)) { // The hostname is contained in the domain settings set. + if (domainSettingsSet.contains(newHostName)) { // The hostname is contained in the domain settings set. // Record the domain name in the database. - domainNameInDatabase = hostName; + domainNameInDatabase = newHostName; // Set the domain settings applied tracker to true. nestedScrollWebView.setDomainSettingsApplied(true); @@ -3638,81 +3509,89 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } // Check all the subdomains of the host name against wildcard domains in the domain cursor. - while (!nestedScrollWebView.getDomainSettingsApplied() && hostName.contains(".")) { // Stop checking if domain settings are already applied or there are no more `.` in the host name. - if (domainSettingsSet.contains("*." + hostName)) { // Check the host name prepended by `*.`. + while (!nestedScrollWebView.getDomainSettingsApplied() && newHostName.contains(".")) { // Stop checking if domain settings are already applied or there are no more `.` in the host name. + if (domainSettingsSet.contains("*." + newHostName)) { // Check the host name prepended by `*.`. // Set the domain settings applied tracker to true. nestedScrollWebView.setDomainSettingsApplied(true); // Store the applied domain names as it appears in the database. - domainNameInDatabase = "*." + hostName; + domainNameInDatabase = "*." + newHostName; } // Strip out the lowest subdomain of of the host name. - hostName = hostName.substring(hostName.indexOf(".") + 1); + newHostName = newHostName.substring(newHostName.indexOf(".") + 1); } - // Get a handle for the shared preference. + // Get a handle for the shared preferences. SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); // Store the general preference information. 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)); - defaultCustomUserAgentString = sharedPreferences.getString("custom_user_agent", getString(R.string.custom_user_agent_default_value)); // TODO. 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(); + if (nestedScrollWebView.getDomainSettingsApplied()) { // The url has custom domain settings. // Get a cursor for the current host and move it to the first position. - Cursor currentHostDomainSettingsCursor = domainsDatabaseHelper.getCursorForDomainName(domainNameInDatabase); - currentHostDomainSettingsCursor.moveToFirst(); + Cursor currentDomainSettingsCursor = domainsDatabaseHelper.getCursorForDomainName(domainNameInDatabase); + currentDomainSettingsCursor.moveToFirst(); // Get the settings from the cursor. - nestedScrollWebView.setDomainSettingsDatabaseId(currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper._ID))); - javaScriptEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_JAVASCRIPT)) == 1); // TODO. - firstPartyCookiesEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FIRST_PARTY_COOKIES)) == 1); // TODO. - thirdPartyCookiesEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_THIRD_PARTY_COOKIES)) == 1); // TODO. - domStorageEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_DOM_STORAGE)) == 1); // TODO. + nestedScrollWebView.setDomainSettingsDatabaseId(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper._ID))); + 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); // Form data can be removed once the minimum API >= 26. - saveFormDataEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FORM_DATA)) == 1); // TODO. - easyListEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_EASYLIST)) == 1); // TODO. - easyPrivacyEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_EASYPRIVACY)) == 1); // TODO. - fanboysAnnoyanceListEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST)) == 1); // TODO. - fanboysSocialBlockingListEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST)) == 1); // TODO. - ultraPrivacyEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY)) == 1); // TODO. - blockAllThirdPartyRequests = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS)) == 1); // TODO. - String userAgentName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.USER_AGENT)); - int fontSize = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.FONT_SIZE)); - int swipeToRefreshInt = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SWIPE_TO_REFRESH)); - int nightModeInt = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.NIGHT_MODE)); - int displayWebpageImagesInt = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.DISPLAY_IMAGES)); - boolean pinnedSslCertificate = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE)) == 1); - String pinnedSslIssuedToCName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME)); - String pinnedSslIssuedToOName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION)); - String pinnedSslIssuedToUName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT)); - String pinnedSslIssuedByCName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME)); - String pinnedSslIssuedByOName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION)); - String pinnedSslIssuedByUName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT)); - boolean pinnedIpAddresses = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_IP_ADDRESSES)) == 1); - String pinnedHostIpAddresses = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.IP_ADDRESSES)); + saveFormDataEnabled = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FORM_DATA)) == 1); // TODO. + nestedScrollWebView.enableBlocklist(NestedScrollWebView.EASY_LIST, + currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_EASYLIST)) == 1); + nestedScrollWebView.enableBlocklist(NestedScrollWebView.EASY_PRIVACY, + currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_EASYPRIVACY)) == 1); + nestedScrollWebView.enableBlocklist(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST, + currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST)) == 1); + nestedScrollWebView.enableBlocklist(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST, + currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST)) == 1); + nestedScrollWebView.enableBlocklist(NestedScrollWebView.ULTRA_PRIVACY, + currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY)) == 1); + nestedScrollWebView.enableBlocklist(NestedScrollWebView.THIRD_PARTY_REQUESTS, + currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS)) == 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 displayWebpageImagesInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.DISPLAY_IMAGES)); + boolean pinnedSslCertificate = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE)) == 1); + String pinnedSslIssuedToCName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME)); + String pinnedSslIssuedToOName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION)); + String pinnedSslIssuedToUName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT)); + String pinnedSslIssuedByCName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME)); + String pinnedSslIssuedByOName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION)); + String pinnedSslIssuedByUName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT)); + 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. 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 (currentHostDomainSettingsCursor.getLong(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE)) == 0) { + if (currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE)) == 0) { pinnedSslStartDate = null; } else { - pinnedSslStartDate = new Date(currentHostDomainSettingsCursor.getLong(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE))); + pinnedSslStartDate = new Date(currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE))); } // 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 (currentHostDomainSettingsCursor.getLong(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE)) == 0) { + if (currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE)) == 0) { pinnedSslEndDate = null; } else { - pinnedSslEndDate = new Date(currentHostDomainSettingsCursor.getLong(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE))); + pinnedSslEndDate = new Date(currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE))); } // If there is a pinned SSL certificate, store it in the WebView. @@ -3739,20 +3618,23 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // TODO. // Store the domain JavaScript status. This is used by the options menu night mode toggle. - domainSettingsJavaScriptEnabled = javaScriptEnabled; + domainSettingsJavaScriptEnabled = domainJavaScriptEnabled; // Enable JavaScript if night mode is enabled. if (nightMode) { - javaScriptEnabled = true; // TODO. + // Enable JavaScript. + nestedScrollWebView.getSettings().setJavaScriptEnabled(true); + } else { + // Set JavaScript according to the domain settings. + nestedScrollWebView.getSettings().setJavaScriptEnabled(domainJavaScriptEnabled); } // Close `currentHostDomainSettingsCursor`. - currentHostDomainSettingsCursor.close(); + currentDomainSettingsCursor.close(); // Apply the domain settings. - nestedScrollWebView.getSettings().setJavaScriptEnabled(javaScriptEnabled); // TODO. cookieManager.setAcceptCookie(firstPartyCookiesEnabled); //TODO This could be bad. - nestedScrollWebView.getSettings().setDomStorageEnabled(domStorageEnabled); // TODO. + nestedScrollWebView.getSettings().setDomStorageEnabled(domainDomStorageEnabled); // TODO. Move up. // Apply the form data setting if the API < 26. if (Build.VERSION.SDK_INT < 26) { @@ -3768,12 +3650,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Set third-party cookies status if API >= 21. if (Build.VERSION.SDK_INT >= 21) { - cookieManager.setAcceptThirdPartyCookies(nestedScrollWebView, thirdPartyCookiesEnabled); // TODO. + cookieManager.setAcceptThirdPartyCookies(nestedScrollWebView, domainThirdPartyCookiesEnabled); } // Only set the user agent if the webpage is not currently loading. Otherwise, changing the user agent on redirects can cause the original website to reload. // - if (!urlIsLoading) { // TODO. We need to track this by WebView. + if (nestedScrollWebView.getProgress() == 100) { // A URL is not loading. // 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. @@ -3792,8 +3674,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook break; case SETTINGS_CUSTOM_USER_AGENT: - // Set the custom user agent. - nestedScrollWebView.getSettings().setUserAgentString(defaultCustomUserAgentString); + // Set the default custom user agent. + nestedScrollWebView.getSettings().setUserAgentString(sharedPreferences.getString("custom_user_agent", getString(R.string.custom_user_agent_default_value))); break; default: @@ -3819,12 +3701,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook nestedScrollWebView.getSettings().setUserAgentString(userAgentDataArray[userAgentArrayPosition]); } } - - // Store the applied user agent string, which is used in the View Source activity. - appliedUserAgentString = nestedScrollWebView.getSettings().getUserAgentString(); // TODO. - - // Update the user agent change tracker. - userAgentChanged = !appliedUserAgentString.equals(initialUserAgent); // TODO. } // Set swipe to refresh. @@ -3859,35 +3735,37 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook break; } - // Set a green background on URL edit text to indicate that custom domain settings are being used. The deprecated `.getDrawable()` must be used until the minimum API >= 21. + // 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 (darkTheme) { - urlEditText.setBackground(getResources().getDrawable(R.drawable.url_bar_background_dark_blue)); + urlRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_dark_blue)); } else { - urlEditText.setBackground(getResources().getDrawable(R.drawable.url_bar_background_light_green)); + urlRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_light_green)); } } else { // The new URL does not have custom domain settings. Load the defaults. - // Store the values from `sharedPreferences` in variables. - javaScriptEnabled = sharedPreferences.getBoolean("javascript", false); // TODO. + // Store the values from the shared preferences. + boolean defaultJavaScriptEnabled = sharedPreferences.getBoolean("javascript", false); firstPartyCookiesEnabled = sharedPreferences.getBoolean("first_party_cookies", false); // TODO. - thirdPartyCookiesEnabled = sharedPreferences.getBoolean("third_party_cookies", false); // TODO. - domStorageEnabled = sharedPreferences.getBoolean("dom_storage", 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. - easyListEnabled = sharedPreferences.getBoolean("easylist", true); // TODO. - easyPrivacyEnabled = sharedPreferences.getBoolean("easyprivacy", true); // TODO. - fanboysAnnoyanceListEnabled = sharedPreferences.getBoolean("fanboys_annoyance_list", true); // TODO. - fanboysSocialBlockingListEnabled = sharedPreferences.getBoolean("fanboys_social_blocking_list", true); // TODO. - ultraPrivacyEnabled = sharedPreferences.getBoolean("ultraprivacy", true); // TODO. - blockAllThirdPartyRequests = sharedPreferences.getBoolean("block_all_third_party_requests", false); // TODO. - - // Set `javaScriptEnabled` to be `true` if `night_mode` is `true`. + 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)); + + // Enable JavaScript if night mode is enabled. if (nightMode) { - javaScriptEnabled = true; // TODO. + // Enable JavaScript. + nestedScrollWebView.getSettings().setJavaScriptEnabled(true); + } else { + // Set JavaScript according to the domain settings. + nestedScrollWebView.getSettings().setJavaScriptEnabled(defaultJavaScriptEnabled); } // Apply the default settings. - nestedScrollWebView.getSettings().setJavaScriptEnabled(javaScriptEnabled); // TODO. cookieManager.setAcceptCookie(firstPartyCookiesEnabled); // TODO. - nestedScrollWebView.getSettings().setDomStorageEnabled(domStorageEnabled); // TODO. nestedScrollWebView.getSettings().setTextZoom(Integer.valueOf(defaultFontSizeString)); swipeRefreshLayout.setEnabled(defaultSwipeToRefresh); @@ -3901,12 +3779,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Set third-party cookies status if API >= 21. if (Build.VERSION.SDK_INT >= 21) { - cookieManager.setAcceptThirdPartyCookies(nestedScrollWebView, thirdPartyCookiesEnabled); + cookieManager.setAcceptThirdPartyCookies(nestedScrollWebView, defaultThirdPartyCookiesEnabled); } // Only set the user agent if the webpage is not currently loading. Otherwise, changing the user agent on redirects can cause the original website to reload. // - if (!urlIsLoading) { // TODO. + if (nestedScrollWebView.getProgress() == 100) { // A URL is not loading. // Get the array position of the user agent name. int userAgentArrayPosition = userAgentNamesArray.getPosition(defaultUserAgentName); @@ -3923,36 +3801,28 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook break; case SETTINGS_CUSTOM_USER_AGENT: - // Set the custom user agent. - nestedScrollWebView.getSettings().setUserAgentString(defaultCustomUserAgentString); // TODO. + // Set the default custom user agent. + nestedScrollWebView.getSettings().setUserAgentString(sharedPreferences.getString("custom_user_agent", getString(R.string.custom_user_agent_default_value))); break; default: // Get the user agent string from the user agent data array nestedScrollWebView.getSettings().setUserAgentString(userAgentDataArray[userAgentArrayPosition]); } - - // Store the applied user agent string, which is used in the View Source activity. - appliedUserAgentString = nestedScrollWebView.getSettings().getUserAgentString(); // TODO. - - // Update the user agent change tracker. - userAgentChanged = !appliedUserAgentString.equals(initialUserAgent); // TODO. } // Set the loading of webpage images. nestedScrollWebView.getSettings().setLoadsImagesAutomatically(displayWebpageImages); - // Set a transparent background on URL edit text. The deprecated `.getDrawable()` must be used until the minimum API >= 21. - urlEditText.setBackgroundDrawable(getResources().getDrawable(R.color.transparent)); + // 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)); } // Close the domains database helper. domainsDatabaseHelper.close(); - // Update the privacy icons, but only if the options menu has already been populated. - if (mainMenu != null) { // TODO. Consider renaming this to optionsMenu. - updatePrivacyIcons(true); - } + // Update the privacy icons. + updatePrivacyIcons(true); } // Reload the website if returning from the Domains activity. @@ -3961,7 +3831,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } // Return the user agent changed status. - return userAgentChanged; + return !nestedScrollWebView.getSettings().getUserAgentString().equals(initialUserAgent); } private void applyProxyThroughOrbot(boolean reloadWebsite) { @@ -4076,59 +3946,62 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } private void updatePrivacyIcons(boolean runInvalidateOptionsMenu) { - // Get handles for the menu items. - MenuItem privacyMenuItem = mainMenu.findItem(R.id.toggle_javascript); - MenuItem firstPartyCookiesMenuItem = mainMenu.findItem(R.id.toggle_first_party_cookies); - MenuItem domStorageMenuItem = mainMenu.findItem(R.id.toggle_dom_storage); - MenuItem refreshMenuItem = mainMenu.findItem(R.id.refresh); - - // Update the privacy icon. - if (javaScriptEnabled) { // JavaScript is enabled. - privacyMenuItem.setIcon(R.drawable.javascript_enabled); - } else if (firstPartyCookiesEnabled) { // JavaScript is disabled but cookies are enabled. - privacyMenuItem.setIcon(R.drawable.warning); - } else { // All the dangerous features are disabled. - privacyMenuItem.setIcon(R.drawable.privacy_mode); - } + // Only update the privacy icons if the options menu has already been populated. + if (optionsMenu != 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); + MenuItem domStorageMenuItem = optionsMenu.findItem(R.id.toggle_dom_storage); + MenuItem refreshMenuItem = optionsMenu.findItem(R.id.refresh); + + // Update the privacy icon. + if (currentWebView.getSettings().getJavaScriptEnabled()) { // JavaScript is enabled. + privacyMenuItem.setIcon(R.drawable.javascript_enabled); + } else if (firstPartyCookiesEnabled) { // JavaScript is disabled but cookies are enabled. + privacyMenuItem.setIcon(R.drawable.warning); + } else { // All the dangerous features are disabled. + privacyMenuItem.setIcon(R.drawable.privacy_mode); + } - // Update the first-party cookies icon. - if (firstPartyCookiesEnabled) { // First-party cookies are enabled. - firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_enabled); - } else { // First-party cookies are disabled. - if (darkTheme) { - firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_disabled_dark); - } else { - firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_disabled_light); + // Update the first-party cookies icon. + if (firstPartyCookiesEnabled) { // First-party cookies are enabled. + firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_enabled); + } else { // First-party cookies are disabled. + if (darkTheme) { + firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_disabled_dark); + } else { + firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_disabled_light); + } } - } - // Update the DOM storage icon. - if (javaScriptEnabled && domStorageEnabled) { // Both JavaScript and DOM storage are enabled. - domStorageMenuItem.setIcon(R.drawable.dom_storage_enabled); - } else if (javaScriptEnabled) { // JavaScript is enabled but DOM storage is disabled. - if (darkTheme) { - domStorageMenuItem.setIcon(R.drawable.dom_storage_disabled_dark); - } else { - domStorageMenuItem.setIcon(R.drawable.dom_storage_disabled_light); + // 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 (darkTheme) { + domStorageMenuItem.setIcon(R.drawable.dom_storage_disabled_dark); + } else { + domStorageMenuItem.setIcon(R.drawable.dom_storage_disabled_light); + } + } else { // JavaScript is disabled, so DOM storage is ghosted. + if (darkTheme) { + domStorageMenuItem.setIcon(R.drawable.dom_storage_ghosted_dark); + } else { + domStorageMenuItem.setIcon(R.drawable.dom_storage_ghosted_light); + } } - } else { // JavaScript is disabled, so DOM storage is ghosted. + + // Update the refresh icon. if (darkTheme) { - domStorageMenuItem.setIcon(R.drawable.dom_storage_ghosted_dark); + refreshMenuItem.setIcon(R.drawable.refresh_enabled_dark); } else { - domStorageMenuItem.setIcon(R.drawable.dom_storage_ghosted_light); + refreshMenuItem.setIcon(R.drawable.refresh_enabled_light); } - } - - // Update the refresh icon. - if (darkTheme) { - refreshMenuItem.setIcon(R.drawable.refresh_enabled_dark); - } else { - refreshMenuItem.setIcon(R.drawable.refresh_enabled_light); - } - // `invalidateOptionsMenu` calls `onPrepareOptionsMenu()` and redraws the icons in the `AppBar`. - if (runInvalidateOptionsMenu) { - invalidateOptionsMenu(); + // `invalidateOptionsMenu` calls `onPrepareOptionsMenu()` and redraws the icons in the `AppBar`. + if (runInvalidateOptionsMenu) { + invalidateOptionsMenu(); + } } } @@ -4290,8 +4163,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } public void addTab(View view) { - // Get a handle for the tab layout. + // 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(); @@ -4305,15 +4179,58 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Remove the lint warning below that the current tab might be null. assert newTab != null; - // Set a custom view on the current tab. + // Set a custom view on the new tab. newTab.setCustomView(R.layout.custom_tab_view); // Add the new WebView page. - webViewPagerAdapter.addPage(newTabNumber); + webViewPagerAdapter.addPage(newTabNumber, webViewPager); + } + + 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); + + // Get the WebView tab fragment. + WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(pageNumber); + + // Get the fragment view. + View fragmentView = webViewTabFragment.getView(); - if (newTabNumber > 0) { - // Move to the new tab. - newTab.select(); + // Remove the incorrect lint warning below that the fragment view might be null. + assert fragmentView != null; + + // Store the current WebView. + currentWebView = fragmentView.findViewById(R.id.nestedscroll_webview); + + // 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(); + + // Hide the soft keyboard. + inputMethodManager.hideSoftInputFromWindow(currentWebView.getWindowToken(), 0); + + // Display the current URL in the URL text box. + urlEditText.setText(formattedUrlString); + + // Highlight the URL text. + highlightUrlText(); + + // Set the background to indicate the domain settings status. + if (currentWebView.getDomainSettingsApplied()) { + // 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 (darkTheme) { + urlRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_dark_blue)); + } else { + urlRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_light_green)); + } + } else { + urlRelativeLayout.setBackground(getResources().getDrawable(R.color.transparent)); } } @@ -4340,6 +4257,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get the relevant preferences. boolean downloadWithExternalApp = sharedPreferences.getBoolean("download_with_external_app", false); + // Initialize the favorite icon. + nestedScrollWebView.initializeFavoriteIcon(); + // Set the app bar scrolling. nestedScrollWebView.setNestedScrollingEnabled(sharedPreferences.getBoolean("scroll_app_bar", true)); @@ -4559,8 +4479,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook public void onReceivedIcon(WebView view, Bitmap icon) { // Only update the favorite icon if the website has finished loading. if (progressBar.getVisibility() == View.GONE) { - // Save a copy of the favorite icon. - favoriteIconBitmap = icon; + // Store the new favorite icon. + nestedScrollWebView.setFavoriteOrDefaultIcon(icon); // Get the current page position. int currentPosition = webViewPagerAdapter.getPositionForId(nestedScrollWebView.getWebViewFragmentId()); @@ -4594,25 +4514,28 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get the current tab. TabLayout.Tab tab = tabLayout.getTabAt(currentPosition); - // Remove the lint warning below that the current tab might be null. - assert tab != null; - - // Get the custom view from the tab. - View tabView = tab.getCustomView(); + // Only populate the title text view if the tab has been fully created. + if (tab != null) { + // Get the custom view from the tab. + View tabView = tab.getCustomView(); - // Remove the incorrect warning below that the current tab view might be null. - assert tabView != null; + // Remove the incorrect warning below that the current tab view might be null. + assert tabView != null; - // Get the title text view from the tab. - TextView tabTitleTextView = tabView.findViewById(R.id.title_textview); + // Get the title text view from the tab. + TextView tabTitleTextView = tabView.findViewById(R.id.title_textview); - // Set the title as the tab text. - tabTitleTextView.setText(title); + // Set the title as the tab text. + tabTitleTextView.setText(title); + } } // Enter full screen video. @Override public void onShowCustomView(View video, CustomViewCallback callback) { + // Get a handle for the full screen video frame layout. + FrameLayout fullScreenVideoFrameLayout = findViewById(R.id.full_screen_video_framelayout); + // Set the full screen video flag. displayingFullScreenVideo = true; @@ -4656,6 +4579,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Exit full screen video. @Override public void onHideCustomView() { + // Get a handle for the full screen video frame layout. + FrameLayout fullScreenVideoFrameLayout = findViewById(R.id.full_screen_video_framelayout); + // Unset the full screen video flag. displayingFullScreenVideo = false; @@ -4810,6 +4736,15 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook @SuppressWarnings("deprecation") @Override public WebResourceResponse shouldInterceptRequest(WebView view, String url) { + // Get a handle for the navigation view. + NavigationView navigationView = findViewById(R.id.navigationview); + + // 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); + // 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())); @@ -4861,13 +4796,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook boolean webViewDisplayed = (webViewPagePosition == tabLayout.getSelectedTabPosition()); // Block third-party requests if enabled. - if (isThirdPartyRequest && blockAllThirdPartyRequests) { + if (isThirdPartyRequest && nestedScrollWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS)) { // Add the result to the resource requests. nestedScrollWebView.addResourceRequest(new String[]{BlockListHelper.REQUEST_THIRD_PARTY, url}); // Increment the blocked requests counters. nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS); - nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.THIRD_PARTY_BLOCKED_REQUESTS); + nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.THIRD_PARTY_REQUESTS); // Update the titles of the blocklist menu items if the WebView is currently displayed. if (webViewDisplayed) { @@ -4876,7 +4811,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Update the menu item titles. navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS)); blocklistsMenuItem.setTitle(getString(R.string.blocklists) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS)); - blockAllThirdPartyRequestsMenuItem.setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.THIRD_PARTY_BLOCKED_REQUESTS) + " - " + + blockAllThirdPartyRequestsMenuItem.setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.THIRD_PARTY_REQUESTS) + " - " + getString(R.string.block_all_third_party_requests)); }); } @@ -4886,7 +4821,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } // Check UltraPrivacy if it is enabled. - if (ultraPrivacyEnabled) { + if (nestedScrollWebView.isBlocklistEnabled(NestedScrollWebView.ULTRA_PRIVACY)) { // Check the URL against UltraPrivacy. String[] ultraPrivacyResults = blockListHelper.checkBlocklist(currentDomain, url, isThirdPartyRequest, ultraPrivacy); @@ -4898,7 +4833,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Increment the blocked requests counters. nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS); - nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.ULTRA_PRIVACY_BLOCKED_REQUESTS); + nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.ULTRA_PRIVACY); // Update the titles of the blocklist menu items if the WebView is currently displayed. if (webViewDisplayed) { @@ -4907,7 +4842,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Update the menu item titles. navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS)); blocklistsMenuItem.setTitle(getString(R.string.blocklists) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS)); - ultraPrivacyMenuItem.setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.ULTRA_PRIVACY_BLOCKED_REQUESTS) + " - " + getString(R.string.ultraprivacy)); + ultraPrivacyMenuItem.setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.ULTRA_PRIVACY) + " - " + getString(R.string.ultraprivacy)); }); } @@ -4924,7 +4859,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } // Check EasyList if it is enabled. - if (easyListEnabled) { + if (nestedScrollWebView.isBlocklistEnabled(NestedScrollWebView.EASY_LIST)) { // Check the URL against EasyList. String[] easyListResults = blockListHelper.checkBlocklist(currentDomain, url, isThirdPartyRequest, easyList); @@ -4935,7 +4870,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Increment the blocked requests counters. nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS); - nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.EASY_LIST_BLOCKED_REQUESTS); + nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.EASY_LIST); // Update the titles of the blocklist menu items if the WebView is currently displayed. if (webViewDisplayed) { @@ -4944,7 +4879,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Update the menu item titles. navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS)); blocklistsMenuItem.setTitle(getString(R.string.blocklists) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS)); - easyListMenuItem.setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.EASY_LIST_BLOCKED_REQUESTS) + " - " + getString(R.string.easylist)); + easyListMenuItem.setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.EASY_LIST) + " - " + getString(R.string.easylist)); }); } @@ -4957,19 +4892,19 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } // Check EasyPrivacy if it is enabled. - if (easyPrivacyEnabled) { + if (nestedScrollWebView.isBlocklistEnabled(NestedScrollWebView.EASY_PRIVACY)) { // Check the URL against EasyPrivacy. String[] easyPrivacyResults = blockListHelper.checkBlocklist(currentDomain, url, isThirdPartyRequest, easyPrivacy); // Process the EasyPrivacy results. if (easyPrivacyResults[0].equals(BlockListHelper.REQUEST_BLOCKED)) { // The resource request matched EasyPrivacy's blacklist. // Add the result to the resource requests. - nestedScrollWebView.addResourceRequest(new String[] {easyPrivacyResults[0], easyPrivacyResults[1], easyPrivacyResults[2], easyPrivacyResults[3], easyPrivacyResults[5], + nestedScrollWebView.addResourceRequest(new String[] {easyPrivacyResults[0], easyPrivacyResults[1], easyPrivacyResults[2], easyPrivacyResults[3], easyPrivacyResults[4], easyPrivacyResults[5]}); // Increment the blocked requests counters. nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS); - nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.EASY_PRIVACY_BLOCKED_REQUESTS); + nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.EASY_PRIVACY); // Update the titles of the blocklist menu items if the WebView is currently displayed. if (webViewDisplayed) { @@ -4978,7 +4913,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Update the menu item titles. navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS)); blocklistsMenuItem.setTitle(getString(R.string.blocklists) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS)); - easyPrivacyMenuItem.setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.EASY_PRIVACY_BLOCKED_REQUESTS) + " - " + getString(R.string.easyprivacy)); + easyPrivacyMenuItem.setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.EASY_PRIVACY) + " - " + getString(R.string.easyprivacy)); }); } @@ -4991,7 +4926,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } // Check Fanboy’s Annoyance List if it is enabled. - if (fanboysAnnoyanceListEnabled) { + if (nestedScrollWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST)) { // Check the URL against Fanboy's Annoyance List. String[] fanboysAnnoyanceListResults = blockListHelper.checkBlocklist(currentDomain, url, isThirdPartyRequest, fanboysAnnoyanceList); @@ -5003,7 +4938,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Increment the blocked requests counters. nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS); - nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST_BLOCKED_REQUESTS); + nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST); // Update the titles of the blocklist menu items if the WebView is currently displayed. if (webViewDisplayed) { @@ -5012,7 +4947,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Update the menu item titles. navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS)); blocklistsMenuItem.setTitle(getString(R.string.blocklists) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS)); - fanboysAnnoyanceListMenuItem.setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST_BLOCKED_REQUESTS) + " - " + + fanboysAnnoyanceListMenuItem.setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST) + " - " + getString(R.string.fanboys_annoyance_list)); }); } @@ -5024,7 +4959,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook whitelistResultStringArray = new String[] {fanboysAnnoyanceListResults[0], fanboysAnnoyanceListResults[1], fanboysAnnoyanceListResults[2], fanboysAnnoyanceListResults[3], fanboysAnnoyanceListResults[4], fanboysAnnoyanceListResults[5]}; } - } else if (fanboysSocialBlockingListEnabled) { // Only check Fanboy’s Social Blocking List if Fanboy’s Annoyance List is disabled. + } else if (nestedScrollWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST)) { // Only check Fanboy’s Social Blocking List if Fanboy’s Annoyance List is disabled. // Check the URL against Fanboy's Annoyance List. String[] fanboysSocialListResults = blockListHelper.checkBlocklist(currentDomain, url, isThirdPartyRequest, fanboysSocialList); @@ -5036,7 +4971,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Increment the blocked requests counters. nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS); - nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST_BLOCKED_REQUESTS); + nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST); // Update the titles of the blocklist menu items if the WebView is currently displayed. if (webViewDisplayed) { @@ -5045,7 +4980,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Update the menu item titles. navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS)); blocklistsMenuItem.setTitle(getString(R.string.blocklists) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS)); - fanboysSocialBlockingListMenuItem.setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST_BLOCKED_REQUESTS) + " - " + + fanboysSocialBlockingListMenuItem.setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST) + " - " + getString(R.string.fanboys_social_blocking_list)); }); } @@ -5083,21 +5018,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { - // Set `urlIsLoading` to `true`, so that redirects while loading do not trigger changes in the user agent, which forces another reload of the existing page. - // This is also used to determine when to check for pinned mismatches. - urlIsLoading = true; - // Reset the list of resource requests. nestedScrollWebView.clearResourceRequests(); - // Initialize the counters for requests blocked by each blocklist. - nestedScrollWebView.resetRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS); - nestedScrollWebView.resetRequestsCount(NestedScrollWebView.EASY_LIST_BLOCKED_REQUESTS); - nestedScrollWebView.resetRequestsCount(NestedScrollWebView.EASY_PRIVACY_BLOCKED_REQUESTS); - nestedScrollWebView.resetRequestsCount(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST_BLOCKED_REQUESTS); - nestedScrollWebView.resetRequestsCount(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST_BLOCKED_REQUESTS); - nestedScrollWebView.resetRequestsCount(NestedScrollWebView.ULTRA_PRIVACY_BLOCKED_REQUESTS); - nestedScrollWebView.resetRequestsCount(NestedScrollWebView.THIRD_PARTY_BLOCKED_REQUESTS); + // Reset the requests counters. + nestedScrollWebView.resetRequestsCounters(); // If night mode is enabled, hide `mainWebView` until after the night mode CSS is applied. if (nightMode) { @@ -5169,7 +5094,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Flush any cookies to persistent storage. `CookieManager` has become very lazy about flushing cookies in recent versions. if (firstPartyCookiesEnabled && Build.VERSION.SDK_INT >= 21) { - cookieManager.flush(); + CookieManager.getInstance().flush(); } // Update the Refresh menu item if it has been created. @@ -5199,11 +5124,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Manually delete cache folders. try { // Delete the main cache directory. - privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/cache"); + Runtime.getRuntime().exec("rm -rf " + privateDataDirectoryString + "/cache"); // 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. - privacyBrowserRuntime.exec(new String[]{"rm", "-rf", privateDataDirectoryString + "/app_webview/Service Worker/"}); + Runtime.getRuntime().exec(new String[]{"rm", "-rf", privateDataDirectoryString + "/app_webview/Service Worker/"}); } catch (IOException e) { // Do nothing if an error is thrown. } @@ -5246,9 +5171,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook CheckPinnedMismatchHelper.checkPinnedMismatch(getSupportFragmentManager(), nestedScrollWebView); } } - - // Reset `urlIsLoading`, which is used to prevent reloads on redirect if the user agent changes. It is also used to determine when to check for pinned mismatches. - urlIsLoading = false; } // Handle SSL Certificate errors.