X-Git-Url: https://gitweb.stoutner.com/?a=blobdiff_plain;f=app%2Fsrc%2Fmain%2Fjava%2Fcom%2Fstoutner%2Fprivacybrowser%2Factivities%2FMainWebViewActivity.java;h=c8d6896b16ad306c1f4018fec468f951fbed2c44;hb=e0827590ff00dc4828c5a607d992b107994ff470;hp=e39b54a440a9d54534f070a082d8327cad9afe4b;hpb=fe788514a50a591f9722ededc13e608ceb268bb8;p=PrivacyBrowserAndroid.git 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 e39b54a4..c8d6896b 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java @@ -168,14 +168,6 @@ 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()`. @@ -218,47 +210,27 @@ 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<>(); // `firstPartyCookiesEnabled` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, `onDownloadImage()`, `onDownloadFile()`, and `applyDomainSettings()`. private boolean firstPartyCookiesEnabled; - // `saveFormDataEnabled` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyDomainSettings()`. It can be removed once the minimum API >= 26. - private boolean saveFormDataEnabled; - - // TODO Move to NestedScrollWebView. - // `nightMode` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyDomainSettings()`. - private boolean nightMode; - // 'homepage' is used in `onCreate()`, `onNavigationItemSelected()`, and `applyProxyThroughOrbot()`. - private String homepage; + private String homepage; // TODO ? // `searchURL` is used in `loadURLFromTextBox()` and `applyProxyThroughOrbot()`. - private String searchURL; + private String searchURL; // TODO ? - // The options menu is set in `onCreateOptionsMenu()` and used in `onOptionsItemSelected()` and `updatePrivacyIcons()`. + // The options menu is set in `onCreateOptionsMenu()` and used in `onOptionsItemSelected()`, `updatePrivacyIcons()`, and `initializeWebView()`. private Menu optionsMenu; - // The refresh menu item is set in `onCreateOptionsMenu()` and accessed from `initializeWebView()`. - // It must be this way because `initializeWebView()` runs before the menu is created but doesn't actually modify the menu until later. - private MenuItem refreshMenuItem; - - // The navigation requests menu item is used in `onCreate()` and accessed from `WebViewPagerAdapter`. - private MenuItem navigationRequestsMenuItem; // TODO. - // TODO. This could probably be removed. // The blocklist helper is used in `onCreate()` and `WebViewPagerAdapter`. private BlockListHelper blockListHelper; @@ -270,7 +242,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook private ArrayList> fanboysSocialList; private ArrayList> ultraPrivacy; - // The blocklist menu items are used in `onCreateOptionsMenu()`, `onPrepareOptionsMenu()`, and `initializeWebView()`. + // The blocklist menu items are used in `onCreateOptionsMenu()`, `onPrepareOptionsMenu()`, and `initializeWebView()`. // TODO. private MenuItem blocklistsMenuItem; private MenuItem easyListMenuItem; private MenuItem easyPrivacyMenuItem; @@ -282,23 +254,20 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `webViewDefaultUserAgent` is used in `onCreate()` and `onPrepareOptionsMenu()`. private String webViewDefaultUserAgent; - // `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; @@ -310,7 +279,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook private boolean displayingFullScreenVideo; // `downloadWithExternalApp` is used in `onCreate()`, `onCreateContextMenu()`, and `applyDomainSettings()`. - private boolean downloadWithExternalApp; + private boolean downloadWithExternalApp; // TODO. // `orbotStatusBroadcastReceiver` is used in `onCreate()` and `onDestroy()`. private BroadcastReceiver orbotStatusBroadcastReceiver; @@ -319,22 +288,19 @@ 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; - - // `findOnPageEditText` is used in `onCreate()`, `onOptionsItemSelected()`, and `closeFindOnPage()`. - private EditText findOnPageEditText; + private String privateDataDirectoryString; // 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; @@ -347,23 +313,20 @@ 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; - - // `bookmarksTitleTextView` is used in `onCreate()` and `loadBookmarksFolder()`. - private TextView bookmarksTitleTextView; + private ListView bookmarksListView; // TODO. // `bookmarksCursor` is used in `onDestroy()`, `onOptionsItemSelected()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and `loadBookmarksFolder()`. private Cursor bookmarksCursor; @@ -385,10 +348,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `downloadImageUrl` is used in `onCreateContextMenu()` and `onRequestPermissionResult()`. private String downloadImageUrl; - // The user agent variables are used in `onCreate()` and `applyDomainSettings()`. - private ArrayAdapter userAgentNamesArray; - private String[] userAgentDataArray; - // The request codes are used in `onCreate()`, `onCreateContextMenu()`, `onCloseDownloadLocationPermissionDialog()`, `onRequestPermissionResult()`, and `initializeWebView()`. private final int DOWNLOAD_FILE_REQUEST_CODE = 1; private final int DOWNLOAD_IMAGE_REQUEST_CODE = 2; @@ -443,7 +402,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook finalGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500)); // Get handles for the URL views. - RelativeLayout urlRelativeLayout = findViewById(R.id.url_relativelayout); EditText urlEditText = findViewById(R.id.url_edittext); // Remove the formatting from `urlTextBar` when the user is editing the text. @@ -524,28 +482,26 @@ 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); bookmarksListView = findViewById(R.id.bookmarks_drawer_listview); - bookmarksTitleTextView = findViewById(R.id.bookmarks_title_textview); FloatingActionButton launchBookmarksActivityFab = findViewById(R.id.launch_bookmarks_activity_fab); FloatingActionButton createBookmarkFolderFab = findViewById(R.id.create_bookmark_folder_fab); FloatingActionButton createBookmarkFab = findViewById(R.id.create_bookmark_fab); - findOnPageEditText = findViewById(R.id.find_on_page_edittext); - fullScreenVideoFrameLayout = findViewById(R.id.full_screen_video_framelayout); + EditText findOnPageEditText = findViewById(R.id.find_on_page_edittext); // 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()); @@ -565,47 +521,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); - - // 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)); - } + // 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) { @@ -642,7 +559,7 @@ 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)); @@ -668,15 +585,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); @@ -684,15 +614,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)); @@ -797,11 +729,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)); } @@ -872,18 +804,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`. @@ -893,8 +819,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Initialize the privacy settings variables. firstPartyCookiesEnabled = false; - saveFormDataEnabled = false; // Form data can be removed once the minimum API >= 26. - nightMode = false; // Inflate a bare WebView to get the default user agent. It is not used to render content on the screen. @SuppressLint("InflateParams") View webViewLayout = getLayoutInflater().inflate(R.layout.bare_webview, null, false); @@ -908,21 +832,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); - // Get the intent that started the app. Intent launchingIntent = getIntent(); @@ -952,53 +861,56 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook @Override protected void onNewIntent(Intent intent) { - // Add a new tab. - addTab(null); - - // 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 @@ -1195,7 +1107,7 @@ 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); - // Store a handle for the options menu so it can be used by `onOptionsItemSelected()` and `updatePrivacyIcons`. + // Store a handle for the options menu so it can be used by `onOptionsItemSelected()` and `updatePrivacyIcons()`. optionsMenu = menu; // Set the initial status of the privacy icons. `false` does not call `invalidateOptionsMenu` as the last step. @@ -1207,7 +1119,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook MenuItem toggleDomStorageMenuItem = menu.findItem(R.id.toggle_dom_storage); MenuItem toggleSaveFormDataMenuItem = menu.findItem(R.id.toggle_save_form_data); // Form data can be removed once the minimum API >= 26. MenuItem clearFormDataMenuItem = menu.findItem(R.id.clear_form_data); // Form data can be removed once the minimum API >= 26. - refreshMenuItem = menu.findItem(R.id.refresh); + MenuItem refreshMenuItem = menu.findItem(R.id.refresh); blocklistsMenuItem = menu.findItem(R.id.blocklists); easyListMenuItem = menu.findItem(R.id.easylist); easyPrivacyMenuItem = menu.findItem(R.id.easyprivacy); @@ -1224,6 +1136,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook toggleSaveFormDataMenuItem.setVisible(Build.VERSION.SDK_INT < 26); clearFormDataMenuItem.setVisible(Build.VERSION.SDK_INT < 26); + // Disable the clear form data menu item if the API >= 26 so that the status of the main Clear Data is calculated correctly. + clearFormDataMenuItem.setEnabled(Build.VERSION.SDK_INT < 26); + // Only show Ad Consent if this is the free flavor. adConsentMenuItem.setVisible(BuildConfig.FLAVOR.contentEquals("free")); @@ -1264,15 +1179,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook @Override public boolean onPrepareOptionsMenu(Menu menu) { - // Get a handle for the swipe refresh layout. - SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout); - // Get handles for the menu items. MenuItem addOrEditDomain = menu.findItem(R.id.add_or_edit_domain); - MenuItem toggleFirstPartyCookiesMenuItem = menu.findItem(R.id.toggle_first_party_cookies); - MenuItem toggleThirdPartyCookiesMenuItem = menu.findItem(R.id.toggle_third_party_cookies); - MenuItem toggleDomStorageMenuItem = menu.findItem(R.id.toggle_dom_storage); - MenuItem toggleSaveFormDataMenuItem = menu.findItem(R.id.toggle_save_form_data); // Form data can be removed once the minimum API >= 26. + MenuItem firstPartyCookiesMenuItem = menu.findItem(R.id.toggle_first_party_cookies); + MenuItem thirdPartyCookiesMenuItem = menu.findItem(R.id.toggle_third_party_cookies); + MenuItem domStorageMenuItem = menu.findItem(R.id.toggle_dom_storage); + MenuItem saveFormDataMenuItem = menu.findItem(R.id.toggle_save_form_data); // Form data can be removed once the minimum API >= 26. MenuItem clearDataMenuItem = menu.findItem(R.id.clear_data); MenuItem clearCookiesMenuItem = menu.findItem(R.id.clear_cookies); MenuItem clearDOMStorageMenuItem = menu.findItem(R.id.clear_dom_storage); @@ -1283,6 +1195,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; @@ -1303,14 +1218,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook fontSize = currentWebView.getSettings().getTextZoom(); // Set the status of the menu item checkboxes. - toggleDomStorageMenuItem.setChecked(currentWebView.getSettings().getDomStorageEnabled()); + domStorageMenuItem.setChecked(currentWebView.getSettings().getDomStorageEnabled()); + saveFormDataMenuItem.setChecked(currentWebView.getSettings().getSaveFormData()); // Form data can be removed once the minimum API >= 26. easyListMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASY_LIST)); easyPrivacyMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASY_PRIVACY)); fanboysAnnoyanceListMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST)); fanboysSocialBlockingListMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST)); ultraPrivacyMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRA_PRIVACY)); blockAllThirdPartyRequestsMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS)); + swipeToRefreshMenuItem.setChecked(currentWebView.getSwipeToRefresh()); displayImagesMenuItem.setChecked(currentWebView.getSettings().getLoadsImagesAutomatically()); + nightModeMenuItem.setChecked(currentWebView.getNightMode()); // Initialize the display names for the blocklists with the number of blocked requests. blocklistsMenuItem.setTitle(getString(R.string.blocklists) + " - " + currentWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS)); @@ -1323,23 +1241,20 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } // Set the status of the menu item checkboxes. - toggleFirstPartyCookiesMenuItem.setChecked(firstPartyCookiesEnabled); - toggleSaveFormDataMenuItem.setChecked(saveFormDataEnabled); // Form data can be removed once the minimum API >= 26. - swipeToRefreshMenuItem.setChecked(swipeRefreshLayout.isEnabled()); - nightModeMenuItem.setChecked(nightMode); + firstPartyCookiesMenuItem.setChecked(firstPartyCookiesEnabled); proxyThroughOrbotMenuItem.setChecked(proxyThroughOrbot); // Only modify third-party cookies if the API >= 21. if (Build.VERSION.SDK_INT >= 21) { // Set the status of the third-party cookies checkbox. - toggleThirdPartyCookiesMenuItem.setChecked(cookieManager.acceptThirdPartyCookies(currentWebView)); + thirdPartyCookiesMenuItem.setChecked(cookieManager.acceptThirdPartyCookies(currentWebView)); // Enable third-party cookies if first-party cookies are enabled. - toggleThirdPartyCookiesMenuItem.setEnabled(firstPartyCookiesEnabled); + thirdPartyCookiesMenuItem.setEnabled(firstPartyCookiesEnabled); } // Enable DOM Storage if JavaScript is enabled. - toggleDomStorageMenuItem.setEnabled(currentWebView.getSettings().getJavaScriptEnabled()); + domStorageMenuItem.setEnabled(currentWebView.getSettings().getJavaScriptEnabled()); // Enable Clear Cookies if there are any. clearCookiesMenuItem.setEnabled(cookieManager.hasCookies()); @@ -1363,11 +1278,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Enable Clear Form Data is there is any. This can be removed once the minimum API >= 26. if (Build.VERSION.SDK_INT < 26) { - WebViewDatabase mainWebViewDatabase = WebViewDatabase.getInstance(this); - clearFormDataMenuItem.setEnabled(mainWebViewDatabase.hasFormData()); - } else { - // Disable clear form data because it is not supported on current version of Android. - clearFormDataMenuItem.setEnabled(false); + // Get the WebView database. + WebViewDatabase webViewDatabase = WebViewDatabase.getInstance(this); + + // Enable the clear form data menu item if there is anything to clear. + clearFormDataMenuItem.setEnabled(webViewDatabase.hasFormData()); } // Enable Clear Data if any of the submenu items are enabled. @@ -1497,6 +1412,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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: @@ -1641,16 +1559,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Form data can be removed once the minimum API >= 26. case R.id.toggle_save_form_data: // Switch the status of saveFormDataEnabled. - saveFormDataEnabled = !saveFormDataEnabled; + currentWebView.getSettings().setSaveFormData(!currentWebView.getSettings().getSaveFormData()); // Update the menu checkbox. - menuItem.setChecked(saveFormDataEnabled); + menuItem.setChecked(currentWebView.getSettings().getSaveFormData()); - // Apply the new form data status. - currentWebView.getSettings().setSaveFormData(saveFormDataEnabled); - - // Display a `Snackbar`. - if (saveFormDataEnabled) { + // Display a snackbar. + if (currentWebView.getSettings().getSaveFormData()) { Snackbar.make(findViewById(R.id.webviewpager), R.string.form_data_enabled, Snackbar.LENGTH_SHORT).show(); } else { Snackbar.make(findViewById(R.id.webviewpager), R.string.form_data_disabled, Snackbar.LENGTH_SHORT).show(); @@ -1719,14 +1634,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(); @@ -1981,11 +1899,25 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook return true; case R.id.swipe_to_refresh: + // Toggle the stored status of swipe to refresh. + currentWebView.setSwipeToRefresh(!currentWebView.getSwipeToRefresh()); + // Get a handle for the swipe refresh layout. SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout); - // Toggle swipe to refresh. - swipeRefreshLayout.setEnabled(!swipeRefreshLayout.isEnabled()); + // Update the swipe refresh layout. + if (currentWebView.getSwipeToRefresh()) { // Swipe to refresh is enabled. + if (Build.VERSION.SDK_INT >= 23) { // For API >= 23, the status of the scroll refresh listener is continuously updated by the on scroll change listener. + // Only enable the swipe refresh layout if the WebView is scrolled to the top. + swipeRefreshLayout.setEnabled(currentWebView.getY() == 0); + } else { // For API < 23, the swipe refresh layout is always enabled. + // Enable the swipe refresh layout. + swipeRefreshLayout.setEnabled(true); + } + } else { // Swipe to refresh is disabled. + // Disable the swipe refresh layout. + swipeRefreshLayout.setEnabled(false); + } return true; case R.id.display_images: @@ -2003,15 +1935,15 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook case R.id.night_mode: // Toggle night mode. - nightMode = !nightMode; + currentWebView.setNightMode(!currentWebView.getNightMode()); // Enable or disable JavaScript according to night mode, the global preference, and any domain settings. - if (nightMode) { // Night mode is enabled, which requires JavaScript. + if (currentWebView.getNightMode()) { // Night mode is enabled, which requires JavaScript. // Enable JavaScript. currentWebView.getSettings().setJavaScriptEnabled(true); } else if (currentWebView.getDomainSettingsApplied()) { // Night mode is disabled and domain settings are applied. Set JavaScript according to the domain settings. // Apply the JavaScript preference that was stored the last time domain settings were loaded. - currentWebView.getSettings().setJavaScriptEnabled(domainSettingsJavaScriptEnabled); + currentWebView.getSettings().setJavaScriptEnabled(domainSettingsJavaScriptEnabled); // TODO. } else { // Night mode is disabled and domain settings are not applied. Set JavaScript according to the global preference. // Apply the JavaScript preference. currentWebView.getSettings().setJavaScriptEnabled(sharedPreferences.getBoolean("javascript", false)); @@ -2028,6 +1960,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get a handle for the views. Toolbar toolbar = findViewById(R.id.toolbar); LinearLayout findOnPageLinearLayout = findViewById(R.id.find_on_page_linearlayout); + EditText findOnPageEditText = findViewById(R.id.find_on_page_edittext); // Hide the toolbar. toolbar.setVisibility(View.GONE); @@ -2094,7 +2027,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)); @@ -2140,8 +2073,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(); @@ -2149,8 +2083,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: @@ -2164,20 +2100,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(); @@ -2196,13 +2135,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(); @@ -2223,8 +2162,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(); @@ -2257,11 +2196,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(); @@ -2306,7 +2245,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(); @@ -2766,7 +2705,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); @@ -2775,19 +2714,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(); @@ -2809,7 +2740,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); @@ -2832,13 +2763,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. @@ -2870,7 +2796,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); @@ -2884,19 +2810,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(); @@ -2913,7 +2831,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); @@ -2942,13 +2860,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. @@ -2975,13 +2888,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. @@ -3073,7 +2981,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); @@ -3125,7 +3033,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); @@ -3372,6 +3280,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get a handle for the views. Toolbar toolbar = findViewById(R.id.toolbar); LinearLayout findOnPageLinearLayout = findViewById(R.id.find_on_page_linearlayout); + EditText findOnPageEditText = findViewById(R.id.find_on_page_edittext); // Delete the contents of `find_on_page_edittext`. findOnPageEditText.setText(null); @@ -3486,9 +3395,6 @@ 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. @SuppressLint("SetJavaScriptEnabled") private boolean applyDomainSettings(NestedScrollWebView nestedScrollWebView, String url, boolean resetFavoriteIcon, boolean reloadWebsite) { - // Get a handle for the URL relative layout. - RelativeLayout urlRelativeLayout = findViewById(R.id.url_relativelayout); - // Store a copy of the current user agent to track changes for the return boolean. String initialUserAgent = nestedScrollWebView.getSettings().getUserAgentString(); @@ -3517,8 +3423,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. + nestedScrollWebView.initializeFavoriteIcon(); // Get a handle for the tab layout. TabLayout tabLayout = findViewById(R.id.tablayout); @@ -3539,12 +3445,9 @@ 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. - SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout); - // Initialize the database handler. The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`. DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(this, null, null, 0); @@ -3606,9 +3509,19 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook String defaultFontSizeString = sharedPreferences.getString("font_size", getString(R.string.font_size_default_value)); String defaultUserAgentName = sharedPreferences.getString("user_agent", getString(R.string.user_agent_default_value)); boolean defaultSwipeToRefresh = sharedPreferences.getBoolean("swipe_to_refresh", true); - nightMode = sharedPreferences.getBoolean("night_mode", false); // TODO. boolean displayWebpageImages = sharedPreferences.getBoolean("display_webpage_images", true); + // Get a handle for the cookie manager. + CookieManager cookieManager = CookieManager.getInstance(); + + // Get handles for the views. + RelativeLayout urlRelativeLayout = findViewById(R.id.url_relativelayout); + SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout); + + // Initialize the user agent array adapter and string array. + ArrayAdapter userAgentNamesArray = ArrayAdapter.createFromResource(this, R.array.user_agent_names, R.layout.spinner_item); + String[] userAgentDataArray = getResources().getStringArray(R.array.user_agent_data); + if (nestedScrollWebView.getDomainSettingsApplied()) { // The url has custom domain settings. // Get a cursor for the current host and move it to the first position. Cursor currentDomainSettingsCursor = domainsDatabaseHelper.getCursorForDomainName(domainNameInDatabase); @@ -3619,9 +3532,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook boolean domainJavaScriptEnabled = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_JAVASCRIPT)) == 1); firstPartyCookiesEnabled = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FIRST_PARTY_COOKIES)) == 1); // TODO. boolean domainThirdPartyCookiesEnabled = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_THIRD_PARTY_COOKIES)) == 1); - boolean domainDomStorageEnabled = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_DOM_STORAGE)) == 1); + nestedScrollWebView.getSettings().setDomStorageEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_DOM_STORAGE)) == 1); // Form data can be removed once the minimum API >= 26. - saveFormDataEnabled = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FORM_DATA)) == 1); // TODO. + boolean saveFormData = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FORM_DATA)) == 1); nestedScrollWebView.enableBlocklist(NestedScrollWebView.EASY_LIST, currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_EASYLIST)) == 1); nestedScrollWebView.enableBlocklist(NestedScrollWebView.EASY_PRIVACY, @@ -3678,14 +3591,21 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook nestedScrollWebView.setPinnedIpAddresses(pinnedHostIpAddresses); } - // Set `nightMode` according to `nightModeInt`. If `nightModeInt` is `DomainsDatabaseHelper.NIGHT_MODE_SYSTEM_DEFAULT` the current setting from `sharedPreferences` will be used. + // Set night mode according to the night mode int. switch (nightModeInt) { + case DomainsDatabaseHelper.NIGHT_MODE_SYSTEM_DEFAULT: + // Set night mode according to the current default. + nestedScrollWebView.setNightMode(sharedPreferences.getBoolean("night_mode", false)); + break; + case DomainsDatabaseHelper.NIGHT_MODE_ENABLED: - nightMode = true; // TODO. + // Enable night mode. + nestedScrollWebView.setNightMode(true); break; case DomainsDatabaseHelper.NIGHT_MODE_DISABLED: - nightMode = false; // TODO. + // Disable night mode. + nestedScrollWebView.setNightMode(false); break; } @@ -3694,7 +3614,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook domainSettingsJavaScriptEnabled = domainJavaScriptEnabled; // Enable JavaScript if night mode is enabled. - if (nightMode) { + if (nestedScrollWebView.getNightMode()) { // Enable JavaScript. nestedScrollWebView.getSettings().setJavaScriptEnabled(true); } else { @@ -3707,11 +3627,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Apply the domain settings. cookieManager.setAcceptCookie(firstPartyCookiesEnabled); //TODO This could be bad. - nestedScrollWebView.getSettings().setDomStorageEnabled(domainDomStorageEnabled); // TODO. Move up. // Apply the form data setting if the API < 26. if (Build.VERSION.SDK_INT < 26) { - nestedScrollWebView.getSettings().setSaveFormData(saveFormDataEnabled); + nestedScrollWebView.getSettings().setSaveFormData(saveFormData); } // Apply the font size. @@ -3732,7 +3651,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Set the user agent. if (userAgentName.equals(getString(R.string.system_default_user_agent))) { // Use the system default user agent. // Get the array position of the default user agent name. - int defaultUserAgentArrayPosition = userAgentNamesArray.getPosition(defaultUserAgentName); // TODO Could this be local. + int defaultUserAgentArrayPosition = userAgentNamesArray.getPosition(defaultUserAgentName); // Set the user agent according to the system default. switch (defaultUserAgentArrayPosition) { @@ -3777,19 +3696,28 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } // Set swipe to refresh. - switch (swipeToRefreshInt) { // TODO. This needs to be set and updated by tab. + switch (swipeToRefreshInt) { case DomainsDatabaseHelper.SWIPE_TO_REFRESH_SYSTEM_DEFAULT: - // Set swipe to refresh according to the default. + // Store the swipe to refresh status in the nested scroll WebView. + nestedScrollWebView.setSwipeToRefresh(defaultSwipeToRefresh); + + // Apply swipe to refresh according to the default. This can be removed once the minimum API >= 23 because it is continuously set by an on scroll change listener. swipeRefreshLayout.setEnabled(defaultSwipeToRefresh); break; case DomainsDatabaseHelper.SWIPE_TO_REFRESH_ENABLED: - // Enable swipe to refresh. + // Store the swipe to refresh status in the nested scroll WebView. + nestedScrollWebView.setSwipeToRefresh(true); + + // Enable swipe to refresh. This can be removed once the minimum API >= 23 because it is continuously set by an on scroll change listener. swipeRefreshLayout.setEnabled(true); break; case DomainsDatabaseHelper.SWIPE_TO_REFRESH_DISABLED: - // Disable swipe to refresh. + // Store the swipe to refresh status in the nested scroll WebView. + nestedScrollWebView.setSwipeToRefresh(false); + + // Disable swipe to refresh. This can be removed once the minimum API >= 23 because it is continuously set by an on scroll change listener. swipeRefreshLayout.setEnabled(false); } @@ -3820,16 +3748,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook firstPartyCookiesEnabled = sharedPreferences.getBoolean("first_party_cookies", false); // TODO. boolean defaultThirdPartyCookiesEnabled = sharedPreferences.getBoolean("third_party_cookies", false); nestedScrollWebView.getSettings().setDomStorageEnabled(sharedPreferences.getBoolean("dom_storage", false)); - saveFormDataEnabled = sharedPreferences.getBoolean("save_form_data", false); // Form data can be removed once the minimum API >= 26. // TODO. + boolean saveFormData = sharedPreferences.getBoolean("save_form_data", false); // Form data can be removed once the minimum API >= 26. nestedScrollWebView.enableBlocklist(NestedScrollWebView.EASY_LIST, sharedPreferences.getBoolean("easylist", true)); nestedScrollWebView.enableBlocklist(NestedScrollWebView.EASY_PRIVACY, sharedPreferences.getBoolean("easyprivacy", true)); nestedScrollWebView.enableBlocklist(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST, sharedPreferences.getBoolean("fanboys_annoyance_list", true)); nestedScrollWebView.enableBlocklist(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST, sharedPreferences.getBoolean("fanboys_social_blocking_list", true)); nestedScrollWebView.enableBlocklist(NestedScrollWebView.ULTRA_PRIVACY, sharedPreferences.getBoolean("ultraprivacy", true)); nestedScrollWebView.enableBlocklist(NestedScrollWebView.THIRD_PARTY_REQUESTS, sharedPreferences.getBoolean("block_all_third_party_requests", false)); + nestedScrollWebView.setNightMode(sharedPreferences.getBoolean("night_mode", false)); // Enable JavaScript if night mode is enabled. - if (nightMode) { + if (nestedScrollWebView.getNightMode()) { // Enable JavaScript. nestedScrollWebView.getSettings().setJavaScriptEnabled(true); } else { @@ -3840,11 +3769,16 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Apply the default settings. cookieManager.setAcceptCookie(firstPartyCookiesEnabled); // TODO. nestedScrollWebView.getSettings().setTextZoom(Integer.valueOf(defaultFontSizeString)); + + // Store the swipe to refresh status in the nested scroll WebView. + nestedScrollWebView.setSwipeToRefresh(defaultSwipeToRefresh); + + // Apply swipe to refresh according to the default. swipeRefreshLayout.setEnabled(defaultSwipeToRefresh); // Apply the form data setting if the API < 26. if (Build.VERSION.SDK_INT < 26) { - currentWebView.getSettings().setSaveFormData(saveFormDataEnabled); + nestedScrollWebView.getSettings().setSaveFormData(saveFormData); } // Reset the pinned variables. @@ -3945,7 +3879,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Set the proxy. `this` refers to the current activity where an `AlertDialog` might be displayed. OrbotProxyHelper.setProxy(getApplicationContext(), this, "localhost", "8118"); - // Set the `appBar` background to indicate proxying through Orbot is enabled. `this` refers to the context. + // Set the `appBar` background to indicate proxying through Orbot is enabled. if (darkTheme) { actionBar.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.dark_blue_30)); } else { @@ -3985,7 +3919,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Reset the proxy to default. The host is `""` and the port is `"0"`. OrbotProxyHelper.setProxy(getApplicationContext(), this, "", "0"); - // Set the default `appBar` background. `this` refers to the context. + // Set the default `appBar` background. if (darkTheme) { actionBar.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.gray_900)); } else { @@ -4199,6 +4133,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Populate the `ListView` with the adapter. bookmarksListView.setAdapter(bookmarksCursorAdapter); + // Get a handle for the bookmarks title text view. + TextView bookmarksTitleTextView = findViewById(R.id.bookmarks_title_textview); + // Set the bookmarks drawer title. if (currentBookmarksFolder.isEmpty()) { bookmarksTitleTextView.setText(R.string.bookmarks); @@ -4259,6 +4196,72 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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); + SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout); + + //Stop the swipe to refresh indicator if it is running + swipeRefreshLayout.setRefreshing(false); + + // Get the WebView tab fragment. + WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(pageNumber); + + // 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); + + // Update the status of swipe to refresh. + if (currentWebView.getSwipeToRefresh()) { // Swipe to refresh is enabled. + if (Build.VERSION.SDK_INT >= 23) { // For API >= 23, swipe refresh layout is continuously updated with an on scroll change listener and only enabled if the WebView is scrolled to the top. + // Enable the swipe refresh layout if the WebView is scrolled all the way to the top. + swipeRefreshLayout.setEnabled(currentWebView.getY() == 0); + } else { + // Enable the swipe refresh layout. + swipeRefreshLayout.setEnabled(true); + } + } else { // Swipe to refresh is disabled. + // Disable the swipe refresh layout. + swipeRefreshLayout.setEnabled(false); + } + + // Update the privacy icons. `true` redraws the icons in the app bar. + updatePrivacyIcons(true); + + // Store the current formatted URL string. + formattedUrlString = currentWebView.getUrl(); + + // Clear the focus from the URL text box. + urlEditText.clearFocus(); + + // 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)); + } + } + @Override public void initializeWebView(NestedScrollWebView nestedScrollWebView, int pageNumber, ProgressBar progressBar) { // Get handles for the activity views. @@ -4282,6 +4285,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)); @@ -4443,13 +4449,23 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } }); + if (Build.VERSION.SDK_INT >= 23) { + nestedScrollWebView.setOnScrollChangeListener((View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) -> { + // Update the status of swipe to refresh if it is enabled. + if (nestedScrollWebView.getSwipeToRefresh()) { + // Only enable swipe to refresh if the WebView is scrolled to the top. + swipeRefreshLayout.setEnabled(scrollY == 0); + } + }); + } + // Set the web chrome client. nestedScrollWebView.setWebChromeClient(new WebChromeClient() { // Update the progress bar when a page is loading. @Override public void onProgressChanged(WebView view, int progress) { // Inject the night mode CSS if night mode is enabled. - if (nightMode) { + if (nestedScrollWebView.getNightMode()) { // `background-color: #212121` sets the background to be dark gray. `color: #BDBDBD` sets the text color to be light gray. `box-shadow: none` removes a lower underline on links // used by WordPress. `text-decoration: none` removes all text underlines. `text-shadow: none` removes text shadows, which usually have a hard coded color. // `border: none` removes all borders, which can also be used to underline text. `a {color: #1565C0}` sets links to be a dark blue. @@ -4484,10 +4500,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Hide the progress bar. progressBar.setVisibility(View.GONE); - // Display `mainWebView` if night mode is disabled. - // Because of a race condition between `applyDomainSettings` and `onPageStarted`, when night mode is set by domain settings the `WebView` may be hidden even if night mode is not - // currently enabled. - if (!nightMode) { + // Display the nested scroll WebView if night mode is disabled. + // Because of a race condition between `applyDomainSettings` and `onPageStarted`, + // when night mode is set by domain settings the WebView may be hidden even if night mode is not currently enabled. + if (!nestedScrollWebView.getNightMode()) { nestedScrollWebView.setVisibility(View.VISIBLE); } @@ -4501,8 +4517,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()); @@ -4536,25 +4552,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; @@ -4598,6 +4617,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; @@ -4752,6 +4774,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())); @@ -4906,7 +4937,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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. @@ -5032,7 +5063,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook nestedScrollWebView.resetRequestsCounters(); // If night mode is enabled, hide `mainWebView` until after the night mode CSS is applied. - if (nightMode) { + if (nestedScrollWebView.getNightMode()) { nestedScrollWebView.setVisibility(View.INVISIBLE); } @@ -5073,8 +5104,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } } - // Replace Refresh with Stop if the menu item has been created. (The WebView typically begins loading before the menu items are instantiated.) - if (refreshMenuItem != null) { + // Replace Refresh with Stop if the options menu has been created. (The WebView typically begins loading before the menu items are instantiated.) + if (optionsMenu != null) { + // Get a handle for the refresh menu item. + MenuItem refreshMenuItem = optionsMenu.findItem(R.id.refresh); + // Set the title. refreshMenuItem.setTitle(R.string.stop); @@ -5101,11 +5135,14 @@ 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. - if (refreshMenuItem != null) { + // Update the Refresh menu item if the options menu has been created. + if (optionsMenu != null) { + // Get a handle for the refresh menu item. + MenuItem refreshMenuItem = optionsMenu.findItem(R.id.refresh); + // Reset the Refresh title. refreshMenuItem.setTitle(R.string.refresh); @@ -5131,11 +5168,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. } @@ -5148,7 +5185,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Set `formattedUrlString` to `""`. formattedUrlString = ""; - urlEditText.setText(formattedUrlString); + // Display the hint in the URL edit text. + urlEditText.setText(""); // Request focus for `urlTextBox`. urlEditText.requestFocus(); @@ -5156,6 +5194,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Display the keyboard. inputMethodManager.showSoftInput(urlEditText, 0); + // Hide the WebView, which causes the default background color to be displayed according to the theme. + nestedScrollWebView.setVisibility(View.GONE); + // Apply the domain settings. This clears any settings from the previous domain. applyDomainSettings(nestedScrollWebView, formattedUrlString, true, false); } else { // `WebView` has loaded a webpage.