+ // Apply the new font size.
+ currentWebView.getSettings().setTextZoom(newFontSize);
+ }
+
+ @Override
+ public void onOpen(DialogFragment dialogFragment) {
+ // Get the dialog.
+ Dialog dialog = dialogFragment.getDialog();
+
+ // Remove the incorrect lint warning below that the dialog might be null.
+ assert dialog != null;
+
+ // Get a handle for the file name edit text.
+ EditText fileNameEditText = dialog.findViewById(R.id.file_name_edittext);
+
+ // Get the file path string.
+ openFilePath = fileNameEditText.getText().toString();
+
+ // Check to see if the storage permission is needed.
+ if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { // The storage permission has been granted.
+ // Open the file.
+ currentWebView.loadUrl("file://" + openFilePath);
+ } else { // The storage permission has not been granted.
+ // Get the external private directory file.
+ File externalPrivateDirectoryFile = getExternalFilesDir(null);
+
+ // Remove the incorrect lint error below that the file might be null.
+ assert externalPrivateDirectoryFile != null;
+
+ // Get the external private directory string.
+ String externalPrivateDirectory = externalPrivateDirectoryFile.toString();
+
+ // Check to see if the file path is in the external private directory.
+ if (openFilePath.startsWith(externalPrivateDirectory)) { // the file path is in the external private directory.
+ // Open the file.
+ currentWebView.loadUrl("file://" + openFilePath);
+ } else { // The file path is in a public directory.
+ // Check if the user has previously denied the storage permission.
+ if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { // Show a dialog explaining the request first.
+ // Instantiate the storage permission alert dialog.
+ DialogFragment storagePermissionDialogFragment = StoragePermissionDialog.displayDialog(StoragePermissionDialog.OPEN);
+
+ // Show the storage permission alert dialog. The permission will be requested the the dialog is closed.
+ storagePermissionDialogFragment.show(getSupportFragmentManager(), getString(R.string.storage_permission));
+ } else { // Show the permission request directly.
+ // Request the write external storage permission. The file will be opened when it finishes.
+ ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_OPEN_REQUEST_CODE);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onSaveWebpage(int saveType, DialogFragment dialogFragment) {
+ // Get the dialog.
+ Dialog dialog = dialogFragment.getDialog();
+
+ // Remove the incorrect lint warning below that the dialog might be null.
+ assert dialog != null;
+
+ // Get a handle for the edit texts.
+ EditText urlEditText = dialog.findViewById(R.id.url_edittext);
+ EditText fileNameEditText = dialog.findViewById(R.id.file_name_edittext);
+
+ // Get the strings from the edit texts.
+ saveWebpageUrl = urlEditText.getText().toString();
+ saveWebpageFilePath = fileNameEditText.getText().toString();
+
+ // Check to see if the storage permission is needed.
+ if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { // The storage permission has been granted.
+ //Save the webpage according to the save type.
+ switch (saveType) {
+ case StoragePermissionDialog.SAVE_URL:
+ // Save the URL.
+ new SaveUrl(this, this, saveWebpageFilePath, currentWebView.getSettings().getUserAgentString(), currentWebView.getAcceptFirstPartyCookies()).execute(saveWebpageUrl);
+ break;
+
+ case StoragePermissionDialog.SAVE_AS_ARCHIVE:
+ // Save the webpage archive.
+ currentWebView.saveWebArchive(saveWebpageFilePath);
+ break;
+
+ case StoragePermissionDialog.SAVE_AS_IMAGE:
+ // Save the webpage image.
+ new SaveWebpageImage(this, currentWebView).execute(saveWebpageFilePath);
+ break;
+ }
+ } else { // The storage permission has not been granted.
+ // Get the external private directory file.
+ File externalPrivateDirectoryFile = getExternalFilesDir(null);
+
+ // Remove the incorrect lint error below that the file might be null.
+ assert externalPrivateDirectoryFile != null;
+
+ // Get the external private directory string.
+ String externalPrivateDirectory = externalPrivateDirectoryFile.toString();
+
+ // Check to see if the file path is in the external private directory.
+ if (saveWebpageFilePath.startsWith(externalPrivateDirectory)) { // The file path is in the external private directory.
+ // Save the webpage according to the save type.
+ switch (saveType) {
+ case StoragePermissionDialog.SAVE_URL:
+ // Save the URL.
+ new SaveUrl(this, this, saveWebpageFilePath, currentWebView.getSettings().getUserAgentString(), currentWebView.getAcceptFirstPartyCookies()).execute(saveWebpageUrl);
+ break;
+
+ case StoragePermissionDialog.SAVE_AS_ARCHIVE:
+ // Save the webpage archive.
+ currentWebView.saveWebArchive(saveWebpageFilePath);
+ break;
+
+ case StoragePermissionDialog.SAVE_AS_IMAGE:
+ // Save the webpage image.
+ new SaveWebpageImage(this, currentWebView).execute(saveWebpageFilePath);
+ break;
+ }
+ } else { // The file path is in a public directory.
+ // Check if the user has previously denied the storage permission.
+ if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { // Show a dialog explaining the request first.
+ // Instantiate the storage permission alert dialog.
+ DialogFragment storagePermissionDialogFragment = StoragePermissionDialog.displayDialog(saveType);
+
+ // Show the storage permission alert dialog. The permission will be requested when the dialog is closed.
+ storagePermissionDialogFragment.show(getSupportFragmentManager(), getString(R.string.storage_permission));
+ } else { // Show the permission request directly.
+ switch (saveType) {
+ case StoragePermissionDialog.SAVE_URL:
+ // Request the write external storage permission. The URL will be saved when it finishes.
+ ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_SAVE_URL_REQUEST_CODE);
+
+ case StoragePermissionDialog.SAVE_AS_ARCHIVE:
+ // Request the write external storage permission. The webpage archive will be saved when it finishes.
+ ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_SAVE_AS_ARCHIVE_REQUEST_CODE);
+ break;
+
+ case StoragePermissionDialog.SAVE_AS_IMAGE:
+ // Request the write external storage permission. The webpage image will be saved when it finishes.
+ ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_SAVE_AS_IMAGE_REQUEST_CODE);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onCloseStoragePermissionDialog(int requestType) {
+ switch (requestType) {
+ case StoragePermissionDialog.OPEN:
+ // Request the write external storage permission. The file will be opened when it finishes.
+ ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_OPEN_REQUEST_CODE);
+ break;
+
+ case StoragePermissionDialog.SAVE_URL:
+ // Request the write external storage permission. The URL will be saved when it finishes.
+ ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_SAVE_URL_REQUEST_CODE);
+ break;
+
+ case StoragePermissionDialog.SAVE_AS_ARCHIVE:
+ // Request the write external storage permission. The webpage archive will be saved when it finishes.
+ ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_SAVE_AS_ARCHIVE_REQUEST_CODE);
+ break;
+
+ case StoragePermissionDialog.SAVE_AS_IMAGE:
+ // Request the write external storage permission. The webpage image will be saved when it finishes.
+ ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_SAVE_AS_IMAGE_REQUEST_CODE);
+ break;
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ //Only process the results if they exist (this method is triggered when a dialog is presented the first time for an app, but no grant results are included).
+ if (grantResults.length > 0) {
+ switch (requestCode) {
+ case PERMISSION_OPEN_REQUEST_CODE:
+ // Check to see if the storage permission was granted. If the dialog was canceled the grant results will be empty.
+ if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { // The storage permission was granted.
+ // Load the file.
+ currentWebView.loadUrl("file://" + openFilePath);
+ } else { // The storage permission was not granted.
+ // Display an error snackbar.
+ Snackbar.make(currentWebView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show();
+ }
+
+ // Reset the open file path.
+ openFilePath = "";
+ break;
+
+ case PERMISSION_SAVE_URL_REQUEST_CODE:
+ // Check to see if the storage permission was granted. If the dialog was canceled the grant results will be empty.
+ if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { // The storage permission was granted.
+ // Save the raw URL.
+ new SaveUrl(this, this, saveWebpageFilePath, currentWebView.getSettings().getUserAgentString(), currentWebView.getAcceptFirstPartyCookies()).execute(saveWebpageUrl);
+ } else { // The storage permission was not granted.
+ // Display an error snackbar.
+ Snackbar.make(currentWebView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show();
+ }
+
+ // Reset the save strings.
+ saveWebpageUrl = "";
+ saveWebpageFilePath = "";
+ break;
+
+ case PERMISSION_SAVE_AS_ARCHIVE_REQUEST_CODE:
+ // Check to see if the storage permission was granted. If the dialog was canceled the grant results will be empty.
+ if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { // The storage permission was granted.
+ // Save the webpage archive.
+ currentWebView.saveWebArchive(saveWebpageFilePath);
+ } else { // The storage permission was not granted.
+ // Display an error snackbar.
+ Snackbar.make(currentWebView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show();
+ }
+
+ // Reset the save webpage file path.
+ saveWebpageFilePath = "";
+ break;
+
+ case PERMISSION_SAVE_AS_IMAGE_REQUEST_CODE:
+ // Check to see if the storage permission was granted. If the dialog was canceled the grant results will be empty.
+ if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { // The storage permission was granted.
+ // Save the webpage image.
+ new SaveWebpageImage(this, currentWebView).execute(saveWebpageFilePath);
+ } else { // The storage permission was not granted.
+ // Display an error snackbar.
+ Snackbar.make(currentWebView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show();
+ }
+
+ // Reset the save webpage file path.
+ saveWebpageFilePath = "";
+ break;
+ }
+ }
+ }
+
+ private void applyAppSettings() {
+ // Initialize the app if this is the first run. This is done here instead of in `onCreate()` to shorten the time that an unthemed background is displayed on app startup.
+ if (webViewDefaultUserAgent == null) {
+ initializeApp();
+ }
+
+ // Get a handle for the shared preferences.
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+
+ // Store the values from the shared preferences in variables.
+ incognitoModeEnabled = sharedPreferences.getBoolean("incognito_mode", false);
+ boolean doNotTrackEnabled = sharedPreferences.getBoolean("do_not_track", false);
+ sanitizeGoogleAnalytics = sharedPreferences.getBoolean("google_analytics", true);
+ sanitizeFacebookClickIds = sharedPreferences.getBoolean("facebook_click_ids", true);
+ sanitizeTwitterAmpRedirects = sharedPreferences.getBoolean("twitter_amp_redirects", true);
+ proxyMode = sharedPreferences.getString("proxy", getString(R.string.proxy_default_value));
+ fullScreenBrowsingModeEnabled = sharedPreferences.getBoolean("full_screen_browsing_mode", false);
+ hideAppBar = sharedPreferences.getBoolean("hide_app_bar", true);
+ scrollAppBar = sharedPreferences.getBoolean("scroll_app_bar", true);
+
+ // Get the search string.
+ String searchString = sharedPreferences.getString("search", getString(R.string.search_default_value));
+
+ // Set the search string.
+ if (searchString.equals("Custom URL")) { // A custom search string is used.
+ searchURL = sharedPreferences.getString("search_custom_url", getString(R.string.search_custom_url_default_value));
+ } else { // A custom search string is not used.
+ searchURL = searchString;
+ }
+
+ // Get a handle for the app compat delegate.
+ AppCompatDelegate appCompatDelegate = getDelegate();
+
+ // Get handles for the views that need to be modified.
+ FrameLayout rootFrameLayout = findViewById(R.id.root_framelayout);
+ AppBarLayout appBarLayout = findViewById(R.id.appbar_layout);
+ ActionBar actionBar = appCompatDelegate.getSupportActionBar();
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ LinearLayout findOnPageLinearLayout = findViewById(R.id.find_on_page_linearlayout);
+ LinearLayout tabsLinearLayout = findViewById(R.id.tabs_linearlayout);
+ SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
+
+ // Remove the incorrect lint warning below that the action bar might be null.
+ assert actionBar != null;
+
+ // Apply the proxy.
+ applyProxy(false);
+
+ // Set Do Not Track status.
+ if (doNotTrackEnabled) {
+ customHeaders.put("DNT", "1");
+ } else {
+ customHeaders.remove("DNT");
+ }
+
+ // Get the current layout parameters. Using coordinator layout parameters allows the `setBehavior()` command and using app bar layout parameters allows the `setScrollFlags()` command.
+ CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams();
+ AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) toolbar.getLayoutParams();
+ AppBarLayout.LayoutParams findOnPageLayoutParams = (AppBarLayout.LayoutParams) findOnPageLinearLayout.getLayoutParams();
+ AppBarLayout.LayoutParams tabsLayoutParams = (AppBarLayout.LayoutParams) tabsLinearLayout.getLayoutParams();
+
+ // Add the scrolling behavior to the layout parameters.
+ if (scrollAppBar) {
+ // Enable scrolling of the app bar.
+ swipeRefreshLayoutParams.setBehavior(new AppBarLayout.ScrollingViewBehavior());
+ toolbarLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP);
+ findOnPageLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP);
+ tabsLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP);
+ } else {
+ // Disable scrolling of the app bar.
+ swipeRefreshLayoutParams.setBehavior(null);
+ toolbarLayoutParams.setScrollFlags(0);
+ findOnPageLayoutParams.setScrollFlags(0);
+ tabsLayoutParams.setScrollFlags(0);
+
+ // Expand the app bar if it is currently collapsed.
+ appBarLayout.setExpanded(true);
+ }
+
+ // Apply the modified layout parameters.
+ swipeRefreshLayout.setLayoutParams(swipeRefreshLayoutParams);
+ toolbar.setLayoutParams(toolbarLayoutParams);
+ findOnPageLinearLayout.setLayoutParams(findOnPageLayoutParams);
+ tabsLinearLayout.setLayoutParams(tabsLayoutParams);
+
+ // Set the app bar scrolling for each WebView.
+ for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
+ // Get the WebView tab fragment.
+ WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i);
+
+ // Get the fragment view.
+ View fragmentView = webViewTabFragment.getView();
+
+ // Only modify the WebViews if they exist.
+ if (fragmentView != null) {
+ // Get the nested scroll WebView from the tab fragment.
+ NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
+
+ // Set the app bar scrolling.
+ nestedScrollWebView.setNestedScrollingEnabled(scrollAppBar);
+ }
+ }