X-Git-Url: https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=blobdiff_plain;f=app%2Fsrc%2Fmain%2Fjava%2Fcom%2Fstoutner%2Fprivacybrowser%2Factivities%2FMainWebViewActivity.java;h=52d5f69998a21541177f67a634083bbd7795f0ec;hp=d7a742315ee00a84b1e648d36b7760a8aacd975c;hb=32847917d4847968feaff721118cdd8bbc12661d;hpb=24ad510d3dfac1e04d3677179a5dc12629e8571e 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 d7a74231..52d5f699 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java @@ -158,12 +158,16 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; + import java.net.MalformedURLException; import java.net.URL; import java.net.URLDecoder; import java.net.URLEncoder; + import java.text.NumberFormat; + import java.util.ArrayList; +import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -247,39 +251,21 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook private ArrayList> ultraList; private ArrayList> ultraPrivacy; - // `webViewDefaultUserAgent` is used in `onCreate()` and `onPrepareOptionsMenu()`. + // Declare the class variables + private BroadcastReceiver orbotStatusBroadcastReceiver; private String webViewDefaultUserAgent; - - // The incognito mode is set in `applyAppSettings()` and used in `initializeWebView()`. private boolean incognitoModeEnabled; - - // The full screen browsing mode tracker is set it `applyAppSettings()` and used in `initializeWebView()`. private boolean fullScreenBrowsingModeEnabled; - - // `inFullScreenBrowsingMode` is used in `onCreate()`, `onConfigurationChanged()`, and `applyAppSettings()`. private boolean inFullScreenBrowsingMode; - - // The app bar trackers are set in `applyAppSettings()` and used in `initializeWebView()`. + private boolean downloadWithExternalApp; private boolean hideAppBar; private boolean scrollAppBar; - - // The loading new intent tracker is set in `onNewIntent()` and used in `setCurrentWebView()`. + private boolean bottomAppBar; private boolean loadingNewIntent; - - // `reapplyDomainSettingsOnRestart` is used in `onCreate()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onRestart()`, and `onAddDomain()`, . private boolean reapplyDomainSettingsOnRestart; - - // `reapplyAppSettingsOnRestart` is used in `onNavigationItemSelected()` and `onRestart()`. private boolean reapplyAppSettingsOnRestart; - - // `displayingFullScreenVideo` is used in `onCreate()` and `onResume()`. private boolean displayingFullScreenVideo; - - // `orbotStatusBroadcastReceiver` is used in `onCreate()` and `onDestroy()`. - private BroadcastReceiver orbotStatusBroadcastReceiver; - - // The waiting for proxy boolean is used in `onResume()`, `initializeApp()` and `applyProxy()`. - private boolean waitingForProxy = false; + private boolean waitingForProxy; // The action bar drawer toggle is initialized in `onCreate()` and used in `onResume()`. private ActionBarDrawerToggle actionBarDrawerToggle; @@ -315,6 +301,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook private boolean sanitizeFacebookClickIds; private boolean sanitizeTwitterAmpRedirects; + // Define the class variables. + private long lastScrollUpdate = 0; + // Declare the class views. private FrameLayout rootFrameLayout; private DrawerLayout drawerLayout; @@ -341,8 +330,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook private MenuItem navigationRequestsMenuItem; private MenuItem optionsPrivacyMenuItem; private MenuItem optionsRefreshMenuItem; - private MenuItem optionsFirstPartyCookiesMenuItem; - private MenuItem optionsThirdPartyCookiesMenuItem; + private MenuItem optionsCookiesMenuItem; private MenuItem optionsDomStorageMenuItem; private MenuItem optionsSaveFormDataMenuItem; private MenuItem optionsClearDataMenuItem; @@ -405,9 +393,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get a handle for the shared preferences. SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); - // Get the screenshot preference. + // Get the preferences. String appTheme = sharedPreferences.getString("app_theme", getString(R.string.app_theme_default_value)); boolean allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false); + bottomAppBar = sharedPreferences.getBoolean(getString(R.string.bottom_app_bar_key), false); // Get the theme entry values string array. String[] appThemeEntryValuesStringArray = getResources().getStringArray(R.array.app_theme_entry_values); @@ -443,7 +432,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook setTheme(R.style.PrivacyBrowser); // Set the content view. - setContentView(R.layout.main_framelayout); + if (bottomAppBar) { + setContentView(R.layout.main_framelayout_bottom_appbar); + } else { + setContentView(R.layout.main_framelayout_top_appbar); + } // Get handles for the views. rootFrameLayout = findViewById(R.id.root_framelayout); @@ -538,6 +531,15 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Only process the URI if it contains data or it is a web search. If the user pressed the desktop icon after the app was already running the URI will be null. if (intentUriData != null || intentStringExtra != null || isWebSearch) { + // Exit the full screen video if it is displayed. + if (displayingFullScreenVideo) { + // Exit full screen video mode. + exitFullScreenVideo(); + + // Reload the current WebView. Otherwise, it can display entirely black. + currentWebView.reload(); + } + // Get the shared preferences. SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); @@ -822,8 +824,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get handles for the class menu items. optionsPrivacyMenuItem = menu.findItem(R.id.javascript); optionsRefreshMenuItem = menu.findItem(R.id.refresh); - optionsFirstPartyCookiesMenuItem = menu.findItem(R.id.first_party_cookies); - optionsThirdPartyCookiesMenuItem = menu.findItem(R.id.third_party_cookies); + optionsCookiesMenuItem = menu.findItem(R.id.cookies); optionsDomStorageMenuItem = menu.findItem(R.id.dom_storage); optionsSaveFormDataMenuItem = menu.findItem(R.id.save_form_data); // Form data can be removed once the minimum API >= 26. optionsClearDataMenuItem = menu.findItem(R.id.clear_data); @@ -871,9 +872,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Set the initial status of the privacy icons. `false` does not call `invalidateOptionsMenu` as the last step. updatePrivacyIcons(false); - // Only display third-party cookies if API >= 21 - optionsThirdPartyCookiesMenuItem.setVisible(Build.VERSION.SDK_INT >= 21); - // Only display the form data menu items if the API < 26. optionsSaveFormDataMenuItem.setVisible(Build.VERSION.SDK_INT < 26); optionsClearFormDataMenuItem.setVisible(Build.VERSION.SDK_INT < 26); @@ -897,11 +895,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook if (displayAdditionalAppBarIcons) { optionsRefreshMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); bookmarksMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); - optionsFirstPartyCookiesMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + optionsCookiesMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); } else { //Do not display the additional icons. optionsRefreshMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); bookmarksMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); - optionsFirstPartyCookiesMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); + optionsCookiesMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); } // Replace Refresh with Stop if a URL is already loading. @@ -975,15 +973,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook optionsUltraPrivacyMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.ULTRAPRIVACY) + " - " + getString(R.string.ultraprivacy)); optionsBlockAllThirdPartyRequestsMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.THIRD_PARTY_REQUESTS) + " - " + getString(R.string.block_all_third_party_requests)); - // Only modify third-party cookies if the API >= 21. - if (Build.VERSION.SDK_INT >= 21) { - // Set the status of the third-party cookies checkbox. - optionsThirdPartyCookiesMenuItem.setChecked(cookieManager.acceptThirdPartyCookies(currentWebView)); - - // Enable third-party cookies if first-party cookies are enabled. - optionsThirdPartyCookiesMenuItem.setEnabled(cookieManager.acceptCookie()); - } - // Enable DOM Storage if JavaScript is enabled. optionsDomStorageMenuItem.setEnabled(currentWebView.getSettings().getJavaScriptEnabled()); @@ -993,8 +982,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } } - // Set the checked status of the first party cookies menu item. - optionsFirstPartyCookiesMenuItem.setChecked(cookieManager.acceptCookie()); + // Set the cookies menu item checked status. + optionsCookiesMenuItem.setChecked(cookieManager.acceptCookie()); // Enable Clear Cookies if there are any. optionsClearCookiesMenuItem.setEnabled(cookieManager.hasCookies()); @@ -1213,12 +1202,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Consume the event. return true; - } else if (menuItemId == R.id.first_party_cookies) { // First-party cookies. + } else if (menuItemId == R.id.cookies) { // Cookies. // Switch the first-party cookie status. cookieManager.setAcceptCookie(!cookieManager.acceptCookie()); - // Store the first-party cookie status. - currentWebView.setAcceptFirstPartyCookies(cookieManager.acceptCookie()); + // Store the cookie status. + currentWebView.setAcceptCookies(cookieManager.acceptCookie()); // Update the menu checkbox. menuItem.setChecked(cookieManager.acceptCookie()); @@ -1227,10 +1216,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook updatePrivacyIcons(true); // Display a snackbar. - if (cookieManager.acceptCookie()) { // First-party cookies are enabled. - Snackbar.make(webViewPager, R.string.first_party_cookies_enabled, Snackbar.LENGTH_SHORT).show(); + if (cookieManager.acceptCookie()) { // Cookies are enabled. + Snackbar.make(webViewPager, R.string.cookies_enabled, Snackbar.LENGTH_SHORT).show(); } else if (currentWebView.getSettings().getJavaScriptEnabled()) { // JavaScript is still enabled. - Snackbar.make(webViewPager, R.string.first_party_cookies_disabled, Snackbar.LENGTH_SHORT).show(); + Snackbar.make(webViewPager, R.string.cookies_disabled, Snackbar.LENGTH_SHORT).show(); } else { // Privacy mode. Snackbar.make(webViewPager, R.string.privacy_mode, Snackbar.LENGTH_SHORT).show(); } @@ -1238,28 +1227,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Reload the current WebView. currentWebView.reload(); - // Consume the event. - return true; - } else if (menuItemId == R.id.third_party_cookies) { // Third-party cookies. - // Only act if the API >= 21. Otherwise, there are no third-party cookie controls. - if (Build.VERSION.SDK_INT >= 21) { - // Toggle the status of thirdPartyCookiesEnabled. - cookieManager.setAcceptThirdPartyCookies(currentWebView, !cookieManager.acceptThirdPartyCookies(currentWebView)); - - // Update the menu checkbox. - menuItem.setChecked(cookieManager.acceptThirdPartyCookies(currentWebView)); - - // Display a snackbar. - if (cookieManager.acceptThirdPartyCookies(currentWebView)) { - Snackbar.make(webViewPager, R.string.third_party_cookies_enabled, Snackbar.LENGTH_SHORT).show(); - } else { - Snackbar.make(webViewPager, R.string.third_party_cookies_disabled, Snackbar.LENGTH_SHORT).show(); - } - - // Reload the current WebView. - currentWebView.reload(); - } - // Consume the event. return true; } else if (menuItemId == R.id.dom_storage) { // DOM storage. @@ -1438,7 +1405,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Update the menu checkbox. menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST)); - // Update the staus of Fanboy's Social Blocking List. + // Update the status of Fanboy's Social Blocking List. optionsFanboysSocialBlockingListMenuItem.setEnabled(!currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST)); // Reload the current WebView. @@ -1756,9 +1723,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Consume the event. return true; } else if (menuItemId == R.id.save_url) { // Save URL. - // Prepare the save dialog. The dialog will be displayed once the file size and the content disposition have been acquired. - new PrepareSaveDialog(this, this, getSupportFragmentManager(), SaveWebpageDialog.SAVE_URL, currentWebView.getSettings().getUserAgentString(), - currentWebView.getAcceptFirstPartyCookies()).execute(currentWebView.getCurrentUrl()); + // Check the download preference. + if (downloadWithExternalApp) { // Download with an external app. + downloadUrlWithExternalApp(currentWebView.getCurrentUrl()); + } else { // Handle the download inside of Privacy Browser. + // Prepare the save dialog. The dialog will be displayed once the file size and the content disposition have been acquired. + new PrepareSaveDialog(this, this, getSupportFragmentManager(), SaveWebpageDialog.SAVE_URL, currentWebView.getSettings().getUserAgentString(), + currentWebView.getAcceptCookies()).execute(currentWebView.getCurrentUrl()); + } // Consume the event. return true; @@ -2156,7 +2128,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook ultraList.get(0).get(0)[0], ultraPrivacy.get(0).get(0)[0]}; // Add the blocklist versions to the intent. - aboutIntent.putExtra("blocklist_versions", blocklistVersions); + aboutIntent.putExtra(AboutActivity.BLOCKLIST_VERSIONS, blocklistVersions); // Make it so. startActivity(aboutIntent); @@ -2269,9 +2241,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Add a Save URL entry. menu.add(R.string.save_url).setOnMenuItemClickListener((MenuItem item) -> { - // Prepare the save dialog. The dialog will be displayed once the file size and the content disposition have been acquired. - new PrepareSaveDialog(this, this, getSupportFragmentManager(), SaveWebpageDialog.SAVE_URL, currentWebView.getSettings().getUserAgentString(), - currentWebView.getAcceptFirstPartyCookies()).execute(linkUrl); + // Check the download preference. + if (downloadWithExternalApp) { // Download with an external app. + downloadUrlWithExternalApp(linkUrl); + } else { // Handle the download inside of Privacy Browser. + // Prepare the save dialog. The dialog will be displayed once the file size and the content disposition have been acquired. + new PrepareSaveDialog(this, this, getSupportFragmentManager(), SaveWebpageDialog.SAVE_URL, currentWebView.getSettings().getUserAgentString(), + currentWebView.getAcceptCookies()).execute(linkUrl); + } // Consume the event. return true; @@ -2336,9 +2313,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Add a Save Image entry. menu.add(R.string.save_image).setOnMenuItemClickListener((MenuItem item) -> { - // Prepare the save dialog. The dialog will be displayed once the file size and the content disposition have been acquired. - new PrepareSaveDialog(this, this, getSupportFragmentManager(), SaveWebpageDialog.SAVE_URL, currentWebView.getSettings().getUserAgentString(), - currentWebView.getAcceptFirstPartyCookies()).execute(imageUrl); + // Check the download preference. + if (downloadWithExternalApp) { // Download with an external app. + downloadUrlWithExternalApp(imageUrl); + } else { // Handle the download inside of Privacy Browser. + // Prepare the save dialog. The dialog will be displayed once the file size and the content disposition have been acquired. + new PrepareSaveDialog(this, this, getSupportFragmentManager(), SaveWebpageDialog.SAVE_URL, currentWebView.getSettings().getUserAgentString(), + currentWebView.getAcceptCookies()).execute(imageUrl); + } // Consume the event. return true; @@ -2436,9 +2418,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Add a Save Image entry. menu.add(R.string.save_image).setOnMenuItemClickListener((MenuItem item) -> { - // Prepare the save dialog. The dialog will be displayed once the file size and the content disposition have been acquired. - new PrepareSaveDialog(this, this, getSupportFragmentManager(), SaveWebpageDialog.SAVE_URL, currentWebView.getSettings().getUserAgentString(), - currentWebView.getAcceptFirstPartyCookies()).execute(imageUrl); + // Check the download preference. + if (downloadWithExternalApp) { // Download with an external app. + downloadUrlWithExternalApp(imageUrl); + } else { // Handle the download inside of Privacy Browser. + // Prepare the save dialog. The dialog will be displayed once the file size and the content disposition have been acquired. + new PrepareSaveDialog(this, this, getSupportFragmentManager(), SaveWebpageDialog.SAVE_URL, currentWebView.getSettings().getUserAgentString(), + currentWebView.getAcceptCookies()).execute(imageUrl); + } // Consume the event. return true; @@ -2458,9 +2445,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Add a Save URL entry. menu.add(R.string.save_url).setOnMenuItemClickListener((MenuItem item) -> { - // Prepare the save dialog. The dialog will be displayed once the file size and the content disposition have been acquired. - new PrepareSaveDialog(this, this, getSupportFragmentManager(), SaveWebpageDialog.SAVE_URL, currentWebView.getSettings().getUserAgentString(), - currentWebView.getAcceptFirstPartyCookies()).execute(linkUrl); + // Check the download preference. + if (downloadWithExternalApp) { // Download with an external app. + downloadUrlWithExternalApp(linkUrl); + } else { // Handle the download inside of Privacy Browser. + // Prepare the save dialog. The dialog will be displayed once the file size and the content disposition have been acquired. + new PrepareSaveDialog(this, this, getSupportFragmentManager(), SaveWebpageDialog.SAVE_URL, currentWebView.getSettings().getUserAgentString(), + currentWebView.getAcceptCookies()).execute(linkUrl); + } // Consume the event. return true; @@ -2729,62 +2721,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // close the bookmarks drawer. drawerLayout.closeDrawer(GravityCompat.END); } else if (displayingFullScreenVideo) { // A full screen video is shown. - // Re-enable the screen timeout. - fullScreenVideoFrameLayout.setKeepScreenOn(false); - - // Unset the full screen video flag. - displayingFullScreenVideo = false; - - // Remove all the views from the full screen video frame layout. - fullScreenVideoFrameLayout.removeAllViews(); - - // Hide the full screen video frame layout. - fullScreenVideoFrameLayout.setVisibility(View.GONE); - - // Enable the sliding drawers. - drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED); - - // Show the main content relative layout. - mainContentRelativeLayout.setVisibility(View.VISIBLE); - - // Apply the appropriate full screen mode flags. - if (fullScreenBrowsingModeEnabled && inFullScreenBrowsingMode) { // Privacy Browser is currently in full screen browsing mode. - // Hide the banner ad in the free flavor. - if (BuildConfig.FLAVOR.contentEquals("free")) { - // Get a handle for the ad view. This cannot be a class variable because it changes with each ad load. - View adView = findViewById(R.id.adview); - - // Hide the banner ad. - AdHelper.hideAd(adView); - } - - /* Hide the system bars. - * SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen. - * SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN makes the root frame layout fill the area that is normally reserved for the status bar. - * SYSTEM_UI_FLAG_HIDE_NAVIGATION hides the navigation bar on the bottom or right of the screen. - * SYSTEM_UI_FLAG_IMMERSIVE_STICKY makes the status and navigation bars translucent and automatically re-hides them after they are shown. - */ - rootFrameLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | - View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); - - // Reload the website if the app bar is hidden. Otherwise, there is some bug in Android that causes the WebView to be entirely black. - if (hideAppBar) { - // Reload the WebView. - currentWebView.reload(); - } - } else { // Switch to normal viewing mode. - // Remove the `SYSTEM_UI` flags from the root frame layout. - rootFrameLayout.setSystemUiVisibility(0); - } - - // Reload the ad for the free flavor if not in full screen mode. - if (BuildConfig.FLAVOR.contentEquals("free") && !inFullScreenBrowsingMode) { - // Get a handle for the ad view. This cannot be a class variable because it changes with each ad load. - View adView = findViewById(R.id.adview); - - // Reload the ad. `getContext()` can be used instead of `getActivity.getApplicationContext()` once the minimum API >= 23. - AdHelper.loadAd(adView, getApplicationContext(), this, getString(R.string.ad_unit_id)); - } + // Exit the full screen video. + exitFullScreenVideo(); } else if (currentWebView.canGoBack()) { // There is at least one item in the current WebView history. // Get the current web back forward list. WebBackForwardList webBackForwardList = currentWebView.copyBackForwardList(); @@ -3098,7 +3036,20 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } } - @Override + private void downloadUrlWithExternalApp(String url) { + // Create a download intent. Not specifying the action type will display the maximum number of options. + Intent downloadIntent = new Intent(); + + // Set the URI and the mime type. + downloadIntent.setDataAndType(Uri.parse(url), "text/html"); + + // Flag the intent to open in a new task. + downloadIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + // Show the chooser. + startActivity(Intent.createChooser(downloadIntent, getString(R.string.download_with_external_app))); + } + public void onSaveWebpage(int saveType, @NonNull String originalUrlString, DialogFragment dialogFragment) { // Get the dialog. Dialog dialog = dialogFragment.getDialog(); @@ -3131,7 +3082,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } // Save the URL. - new SaveUrl(this, this, saveWebpageFilePath, currentWebView.getSettings().getUserAgentString(), currentWebView.getAcceptFirstPartyCookies()).execute(saveWebpageUrl); + new SaveUrl(this, this, saveWebpageFilePath, currentWebView.getSettings().getUserAgentString(), currentWebView.getAcceptCookies()).execute(saveWebpageUrl); break; case SaveWebpageDialog.SAVE_ARCHIVE: @@ -3191,7 +3142,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook break; } } - + + // Remove the warning that `OnTouchListener()` needs to override `performClick()`, as the only purpose of setting the `OnTouchListener()` is to make it do nothing. + @SuppressLint("ClickableViewAccessibility") private void initializeApp() { // Get a handle for the input method. InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); @@ -3301,6 +3254,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook this.registerReceiver(orbotStatusBroadcastReceiver, new IntentFilter("org.torproject.android.intent.action.STATUS")); // Get handles for views that need to be modified. + LinearLayout bookmarksHeaderLinearLayout = findViewById(R.id.bookmarks_header_linearlayout); ListView bookmarksListView = findViewById(R.id.bookmarks_drawer_listview); FloatingActionButton launchBookmarksActivityFab = findViewById(R.id.launch_bookmarks_activity_fab); FloatingActionButton createBookmarkFolderFab = findViewById(R.id.create_bookmark_folder_fab); @@ -3367,6 +3321,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } }); + // Set a touch listener on the bookmarks header linear layout so that touches don't pass through to the button underneath. + bookmarksHeaderLinearLayout.setOnTouchListener((view, motionEvent) -> { + // Consume the touch. + return true; + }); + // Set the launch bookmarks activity FAB to launch the bookmarks activity. launchBookmarksActivityFab.setOnClickListener(v -> { // Get a copy of the favorite icon bitmap. @@ -3448,7 +3408,19 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook }); // Implement swipe to refresh. - swipeRefreshLayout.setOnRefreshListener(() -> currentWebView.reload()); + swipeRefreshLayout.setOnRefreshListener(() -> { + // Check the visibility of the bottom app bar. Sometimes it is hidden if the WebView is the same size as the visible screen. + if (bottomAppBar && scrollAppBar && (appBarLayout.getVisibility() == View.GONE)) { // The bottom app bar is currently hidden. + // Show the app bar. + appBarLayout.setVisibility(View.VISIBLE); + + // Disable the refreshing animation. + swipeRefreshLayout.setRefreshing(false); + } else { // A bottom app bar is not currently hidden. + // Reload the website. + currentWebView.reload(); + } + }); // Store the default progress view offsets for use later in `initializeWebView()`. defaultProgressViewStartOffset = swipeRefreshLayout.getProgressViewStartOffset(); @@ -3522,7 +3494,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Find out if the selected bookmark is a folder. boolean isFolder = bookmarksDatabaseHelper.isFolder(databaseId); - if (isFolder) { + // Check to see if the bookmark is a folder. + if (isFolder) { // The bookmark is a folder. // Save the current folder name, which is used in `onSaveEditBookmarkFolder()`. oldFolderNameString = bookmarksCursor.getString(bookmarksCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME)); @@ -3531,7 +3504,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Show the edit folder bookmark dialog. editBookmarkFolderDialog.show(getSupportFragmentManager(), getString(R.string.edit_folder)); - } else { + } else { // The bookmark is not a folder. // Get the bookmark cursor for this ID. Cursor bookmarkCursor = bookmarksDatabaseHelper.getBookmark(databaseId); @@ -3540,6 +3513,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Load the bookmark in a new tab but do not switch to the tab or close the drawer. addNewTab(bookmarkCursor.getString(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_URL)), false); + + // Display a snackbar. + Snackbar.make(currentWebView, R.string.bookmark_opened_in_background, Snackbar.LENGTH_SHORT).show(); } // Consume the event. @@ -3605,12 +3581,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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); + downloadWithExternalApp = sharedPreferences.getBoolean(getString(R.string.download_with_external_app_key), false); hideAppBar = sharedPreferences.getBoolean("hide_app_bar", true); scrollAppBar = sharedPreferences.getBoolean("scroll_app_bar", true); @@ -3636,58 +3612,48 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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); + // Adjust the layout and scrolling parameters if the app bar is at the top of the screen. + if (!bottomAppBar) { + // 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); + } - // 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); + // 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(); + // 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); + // 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); + // Set the app bar scrolling. + nestedScrollWebView.setNestedScrollingEnabled(scrollAppBar); + } } } @@ -3917,8 +3883,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get the settings from the cursor. nestedScrollWebView.setDomainSettingsDatabaseId(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper._ID))); nestedScrollWebView.getSettings().setJavaScriptEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_JAVASCRIPT)) == 1); - nestedScrollWebView.setAcceptFirstPartyCookies(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FIRST_PARTY_COOKIES)) == 1); - boolean domainThirdPartyCookiesEnabled = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_THIRD_PARTY_COOKIES)) == 1); + nestedScrollWebView.setAcceptCookies(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.COOKIES)) == 1); nestedScrollWebView.getSettings().setDomStorageEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_DOM_STORAGE)) == 1); // Form data can be removed once the minimum API >= 26. boolean saveFormData = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FORM_DATA)) == 1); @@ -3988,12 +3953,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } // Apply the cookie domain settings. - cookieManager.setAcceptCookie(nestedScrollWebView.getAcceptFirstPartyCookies()); - - // Set third-party cookies status if API >= 21. - if (Build.VERSION.SDK_INT >= 21) { - cookieManager.setAcceptThirdPartyCookies(nestedScrollWebView, domainThirdPartyCookiesEnabled); - } + cookieManager.setAcceptCookie(nestedScrollWebView.getAcceptCookies()); // Apply the form data setting if the API < 26. if (Build.VERSION.SDK_INT < 26) { @@ -4172,8 +4132,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } else { // The new URL does not have custom domain settings. Load the defaults. // Store the values from the shared preferences. nestedScrollWebView.getSettings().setJavaScriptEnabled(sharedPreferences.getBoolean("javascript", false)); - nestedScrollWebView.setAcceptFirstPartyCookies(sharedPreferences.getBoolean("first_party_cookies", false)); - boolean defaultThirdPartyCookiesEnabled = sharedPreferences.getBoolean("third_party_cookies", false); + nestedScrollWebView.setAcceptCookies(sharedPreferences.getBoolean(getString(R.string.cookies_key), false)); nestedScrollWebView.getSettings().setDomStorageEnabled(sharedPreferences.getBoolean("dom_storage", false)); boolean saveFormData = sharedPreferences.getBoolean("save_form_data", false); // Form data can be removed once the minimum API >= 26. nestedScrollWebView.enableBlocklist(NestedScrollWebView.EASYLIST, sharedPreferences.getBoolean("easylist", true)); @@ -4184,8 +4143,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook nestedScrollWebView.enableBlocklist(NestedScrollWebView.ULTRAPRIVACY, sharedPreferences.getBoolean("ultraprivacy", true)); nestedScrollWebView.enableBlocklist(NestedScrollWebView.THIRD_PARTY_REQUESTS, sharedPreferences.getBoolean("block_all_third_party_requests", false)); - // Apply the default first-party cookie setting. - cookieManager.setAcceptCookie(nestedScrollWebView.getAcceptFirstPartyCookies()); + // Apply the default cookie setting. + cookieManager.setAcceptCookie(nestedScrollWebView.getAcceptCookies()); // Apply the default font size setting. try { @@ -4216,11 +4175,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Reset the pinned variables. nestedScrollWebView.setDomainSettingsDatabaseId(-1); - // Set third-party cookies status if API >= 21. - if (Build.VERSION.SDK_INT >= 21) { - cookieManager.setAcceptThirdPartyCookies(nestedScrollWebView, defaultThirdPartyCookiesEnabled); - } - // Get the array position of the user agent name. int userAgentArrayPosition = userAgentNamesArray.getPosition(defaultUserAgentName); @@ -4431,7 +4385,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Update the privacy icon. if (currentWebView.getSettings().getJavaScriptEnabled()) { // JavaScript is enabled. optionsPrivacyMenuItem.setIcon(R.drawable.javascript_enabled); - } else if (currentWebView.getAcceptFirstPartyCookies()) { // JavaScript is disabled but cookies are enabled. + } else if (currentWebView.getAcceptCookies()) { // JavaScript is disabled but cookies are enabled. optionsPrivacyMenuItem.setIcon(R.drawable.warning); } else { // All the dangerous features are disabled. optionsPrivacyMenuItem.setIcon(R.drawable.privacy_mode); @@ -4440,14 +4394,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get the current theme status. int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; - // Update the first-party cookies icon. - if (currentWebView.getAcceptFirstPartyCookies()) { // First-party cookies are enabled. - optionsFirstPartyCookiesMenuItem.setIcon(R.drawable.cookies_enabled); - } else { // First-party cookies are disabled. + // Update the cookies icon. + if (currentWebView.getAcceptCookies()) { // Cookies are enabled. + optionsCookiesMenuItem.setIcon(R.drawable.cookies_enabled); + } else { // Cookies are disabled. if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { - optionsFirstPartyCookiesMenuItem.setIcon(R.drawable.cookies_disabled_day); + optionsCookiesMenuItem.setIcon(R.drawable.cookies_disabled_day); } else { - optionsFirstPartyCookiesMenuItem.setIcon(R.drawable.cookies_disabled_night); + optionsCookiesMenuItem.setIcon(R.drawable.cookies_disabled_night); } } @@ -4537,11 +4491,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Update the bookmarks cursor with the contents of the bookmarks database for the current folder. bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentBookmarksFolder); - // Populate the bookmarks cursor adapter. `this` specifies the `Context`. `false` disables `autoRequery`. + // Populate the bookmarks cursor adapter. bookmarksCursorAdapter = new CursorAdapter(this, bookmarksCursor, false) { @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { - // Inflate the individual item layout. `false` does not attach it to the root. + // Inflate the individual item layout. return getLayoutInflater().inflate(R.layout.bookmarks_drawer_item_linearlayout, parent, false); } @@ -4835,6 +4789,68 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } } + private void exitFullScreenVideo() { + // Re-enable the screen timeout. + fullScreenVideoFrameLayout.setKeepScreenOn(false); + + // Unset the full screen video flag. + displayingFullScreenVideo = false; + + // Remove all the views from the full screen video frame layout. + fullScreenVideoFrameLayout.removeAllViews(); + + // Hide the full screen video frame layout. + fullScreenVideoFrameLayout.setVisibility(View.GONE); + + // Enable the sliding drawers. + drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED); + + // Show the main content relative layout. + mainContentRelativeLayout.setVisibility(View.VISIBLE); + + // Apply the appropriate full screen mode flags. + if (fullScreenBrowsingModeEnabled && inFullScreenBrowsingMode) { // Privacy Browser is currently in full screen browsing mode. + // Hide the app bar if specified. + if (hideAppBar) { + // Hide the tab linear layout. + tabsLinearLayout.setVisibility(View.GONE); + + // Hide the action bar. + actionBar.hide(); + } + + // Hide the banner ad in the free flavor. + if (BuildConfig.FLAVOR.contentEquals("free")) { + // Get a handle for the ad view. This cannot be a class variable because it changes with each ad load. + View adView = findViewById(R.id.adview); + + // Hide the banner ad. + AdHelper.hideAd(adView); + } + + /* Hide the system bars. + * SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen. + * SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN makes the root frame layout fill the area that is normally reserved for the status bar. + * SYSTEM_UI_FLAG_HIDE_NAVIGATION hides the navigation bar on the bottom or right of the screen. + * SYSTEM_UI_FLAG_IMMERSIVE_STICKY makes the status and navigation bars translucent and automatically re-hides them after they are shown. + */ + rootFrameLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); + } else { // Switch to normal viewing mode. + // Remove the `SYSTEM_UI` flags from the root frame layout. + rootFrameLayout.setSystemUiVisibility(0); + } + + // Reload the ad for the free flavor if not in full screen mode. + if (BuildConfig.FLAVOR.contentEquals("free") && !inFullScreenBrowsingMode) { + // Get a handle for the ad view. This cannot be a class variable because it changes with each ad load. + View adView = findViewById(R.id.adview); + + // Reload the ad. + AdHelper.loadAd(adView, this, this, getString(R.string.ad_unit_id)); + } + } + private void clearAndExit() { // Get a handle for the shared preferences. SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); @@ -5068,8 +5084,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get a handle for the cookie manager. CookieManager cookieManager = CookieManager.getInstance(); - // Set the first-party cookie status. - cookieManager.setAcceptCookie(currentWebView.getAcceptFirstPartyCookies()); + // Set the cookie status. + cookieManager.setAcceptCookie(currentWebView.getAcceptCookies()); // Update the privacy icons. `true` redraws the icons in the app bar. updatePrivacyIcons(true); @@ -5244,19 +5260,22 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Hide the action bar. actionBar.hide(); - // Check to see if the app bar is normally scrolled. - if (scrollAppBar) { // The app bar is scrolled when it is displayed. - // Get the swipe refresh layout parameters. - CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams(); - - // Remove the off-screen scrolling layout. - swipeRefreshLayoutParams.setBehavior(null); - } else { // The app bar is not scrolled when it is displayed. - // Remove the padding from the top of the swipe refresh layout. - swipeRefreshLayout.setPadding(0, 0, 0, 0); - - // The swipe refresh circle must be moved above the now removed status bar location. - swipeRefreshLayout.setProgressViewOffset(false, -200, defaultProgressViewEndOffset); + // Set layout and scrolling parameters if the app bar is at the top of the screen. + if (!bottomAppBar) { + // Check to see if the app bar is normally scrolled. + if (scrollAppBar) { // The app bar is scrolled when it is displayed. + // Get the swipe refresh layout parameters. + CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams(); + + // Remove the off-screen scrolling layout. + swipeRefreshLayoutParams.setBehavior(null); + } else { // The app bar is not scrolled when it is displayed. + // Remove the padding from the top of the swipe refresh layout. + swipeRefreshLayout.setPadding(0, 0, 0, 0); + + // The swipe refresh circle must be moved above the now removed status bar location. + swipeRefreshLayout.setProgressViewOffset(false, -200, defaultProgressViewEndOffset); + } } } @@ -5286,19 +5305,22 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Show the action bar. actionBar.show(); - // Check to see if the app bar is normally scrolled. - if (scrollAppBar) { // The app bar is scrolled when it is displayed. - // Get the swipe refresh layout parameters. - CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams(); - - // Add the off-screen scrolling layout. - swipeRefreshLayoutParams.setBehavior(new AppBarLayout.ScrollingViewBehavior()); - } else { // The app bar is not scrolled when it is displayed. - // The swipe refresh layout must be manually moved below the app bar layout. - swipeRefreshLayout.setPadding(0, appBarHeight, 0, 0); - - // The swipe to refresh circle doesn't always hide itself completely unless it is moved up 10 pixels. - swipeRefreshLayout.setProgressViewOffset(false, defaultProgressViewStartOffset - 10 + appBarHeight, defaultProgressViewEndOffset + appBarHeight); + // Set layout and scrolling parameters if the app bar is at the top of the screen. + if (!bottomAppBar) { + // Check to see if the app bar is normally scrolled. + if (scrollAppBar) { // The app bar is scrolled when it is displayed. + // Get the swipe refresh layout parameters. + CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams(); + + // Add the off-screen scrolling layout. + swipeRefreshLayoutParams.setBehavior(new AppBarLayout.ScrollingViewBehavior()); + } else { // The app bar is not scrolled when it is displayed. + // The swipe refresh layout must be manually moved below the app bar layout. + swipeRefreshLayout.setPadding(0, appBarHeight, 0, 0); + + // The swipe to refresh circle doesn't always hide itself completely unless it is moved up 10 pixels. + swipeRefreshLayout.setProgressViewOffset(false, defaultProgressViewStartOffset - 10 + appBarHeight, defaultProgressViewEndOffset + appBarHeight); + } } } @@ -5337,38 +5359,43 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Allow the downloading of files. nestedScrollWebView.setDownloadListener((String downloadUrl, String userAgent, String contentDisposition, String mimetype, long contentLength) -> { - // Define a formatted file size string. - String formattedFileSizeString; - - // Process the content length if it contains data. - if (contentLength > 0) { // The content length is greater than 0. - // Format the content length as a string. - formattedFileSizeString = NumberFormat.getInstance().format(contentLength) + " " + getString(R.string.bytes); - } else { // The content length is not greater than 0. - // Set the formatted file size string to be `unknown size`. - formattedFileSizeString = getString(R.string.unknown_size); - } + // Check the download preference. + if (downloadWithExternalApp) { // Download with an external app. + downloadUrlWithExternalApp(downloadUrl); + } else { // Handle the download inside of Privacy Browser. + // Define a formatted file size string. + String formattedFileSizeString; + + // Process the content length if it contains data. + if (contentLength > 0) { // The content length is greater than 0. + // Format the content length as a string. + formattedFileSizeString = NumberFormat.getInstance().format(contentLength) + " " + getString(R.string.bytes); + } else { // The content length is not greater than 0. + // Set the formatted file size string to be `unknown size`. + formattedFileSizeString = getString(R.string.unknown_size); + } - // Get the file name from the content disposition. - String fileNameString = PrepareSaveDialog.getFileNameFromHeaders(this, contentDisposition, mimetype, downloadUrl); + // Get the file name from the content disposition. + String fileNameString = PrepareSaveDialog.getFileNameFromHeaders(this, contentDisposition, mimetype, downloadUrl); - // Prevent the dialog from displaying if the app window is not visible. - // The download listener continues to function even when the WebView is paused. Attempting to display a dialog in that state leads to a crash. - while (!activity.getWindow().isActive()) { - try { - // The window is not active. Wait 1 second. - wait(1000); - } catch (InterruptedException e) { - // Do nothing. + // Prevent the dialog from displaying if the app window is not visible. + // The download listener continues to function even when the WebView is paused. Attempting to display a dialog in that state leads to a crash. + while (!activity.getWindow().isActive()) { + try { + // The window is not active. Wait 1 second. + wait(1000); + } catch (InterruptedException e) { + // Do nothing. + } } - } - // Instantiate the save dialog. - DialogFragment saveDialogFragment = SaveWebpageDialog.saveWebpage(SaveWebpageDialog.SAVE_URL, downloadUrl, formattedFileSizeString, fileNameString, userAgent, - nestedScrollWebView.getAcceptFirstPartyCookies()); + // Instantiate the save dialog. + DialogFragment saveDialogFragment = SaveWebpageDialog.saveWebpage(SaveWebpageDialog.SAVE_URL, downloadUrl, formattedFileSizeString, fileNameString, userAgent, + nestedScrollWebView.getAcceptCookies()); - // Show the save dialog. It must be named `save_dialog` so that the file picker can update the file name. - saveDialogFragment.show(getSupportFragmentManager(), getString(R.string.save_dialog)); + // Show the save dialog. It must be named `save_dialog` so that the file picker can update the file name. + saveDialogFragment.show(getSupportFragmentManager(), getString(R.string.save_dialog)); + } }); // Update the find on page count. @@ -5397,7 +5424,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Update the status of swipe to refresh based on the scroll position of the nested scroll WebView. Also reinforce full screen browsing mode. // On API < 23, `getViewTreeObserver().addOnScrollChangedListener()` must be used, but it is a little bit buggy and appears to get garbage collected from time to time. if (Build.VERSION.SDK_INT >= 23) { - nestedScrollWebView.setOnScrollChangeListener((view, i, i1, i2, i3) -> { + nestedScrollWebView.setOnScrollChangeListener((view, scrollX, scrollY, oldScrollX, oldScrollY) -> { + // Set the swipe to refresh status. if (nestedScrollWebView.getSwipeToRefresh()) { // Only enable swipe to refresh if the WebView is scrolled to the top. swipeRefreshLayout.setEnabled(nestedScrollWebView.getScrollY() == 0); @@ -5406,6 +5434,18 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook swipeRefreshLayout.setEnabled(false); } + // Set the visibility of the bottom app bar. + if (bottomAppBar && scrollAppBar && (Calendar.getInstance().getTimeInMillis() - lastScrollUpdate > 100)) { + if (scrollY - oldScrollY > 25) { // The WebView was scrolled down. + appBarLayout.setVisibility(View.GONE); + } else if (scrollY - oldScrollY < -25) { // The WebView was scrolled up. + appBarLayout.setVisibility(View.VISIBLE); + } + + // Update the last scroll update variable. This prevents the app bar from flashing on and off at the bottom of the screen. + lastScrollUpdate = Calendar.getInstance().getTimeInMillis(); + } + // Reinforce the system UI visibility flags if in full screen browsing mode. // This hides the status and navigation bars, which are displayed if other elements are shown, like dialog boxes, the options menu, or the keyboard. if (inFullScreenBrowsingMode) { @@ -5429,7 +5469,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook swipeRefreshLayout.setEnabled(false); } - // Reinforce the system UI visibility flags if in full screen browsing mode. // This hides the status and navigation bars, which are displayed if other elements are shown, like dialog boxes, the options menu, or the keyboard. if (inFullScreenBrowsingMode) { @@ -5577,65 +5616,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Exit full screen video. @Override public void onHideCustomView() { - // Re-enable the screen timeout. - fullScreenVideoFrameLayout.setKeepScreenOn(false); - - // Unset the full screen video flag. - displayingFullScreenVideo = false; - - // Remove all the views from the full screen video frame layout. - fullScreenVideoFrameLayout.removeAllViews(); - - // Hide the full screen video frame layout. - fullScreenVideoFrameLayout.setVisibility(View.GONE); - - // Enable the sliding drawers. - drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED); - - // Show the main content relative layout. - mainContentRelativeLayout.setVisibility(View.VISIBLE); - - // Apply the appropriate full screen mode flags. - if (fullScreenBrowsingModeEnabled && inFullScreenBrowsingMode) { // Privacy Browser is currently in full screen browsing mode. - // Hide the app bar if specified. - if (hideAppBar) { - // Hide the tab linear layout. - tabsLinearLayout.setVisibility(View.GONE); - - // Hide the action bar. - actionBar.hide(); - } - - // Hide the banner ad in the free flavor. - if (BuildConfig.FLAVOR.contentEquals("free")) { - // Get a handle for the ad view. This cannot be a class variable because it changes with each ad load. - View adView = findViewById(R.id.adview); - - // Hide the banner ad. - AdHelper.hideAd(adView); - } - - /* Hide the system bars. - * SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen. - * SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN makes the root frame layout fill the area that is normally reserved for the status bar. - * SYSTEM_UI_FLAG_HIDE_NAVIGATION hides the navigation bar on the bottom or right of the screen. - * SYSTEM_UI_FLAG_IMMERSIVE_STICKY makes the status and navigation bars translucent and automatically re-hides them after they are shown. - */ - rootFrameLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | - View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); - } else { // Switch to normal viewing mode. - // Remove the `SYSTEM_UI` flags from the root frame layout. - rootFrameLayout.setSystemUiVisibility(0); - } - - // Reload the ad for the free flavor if not in full screen mode. - if (BuildConfig.FLAVOR.contentEquals("free") && !inFullScreenBrowsingMode) { - // Get a handle for the ad view. This cannot be a class variable because it changes with each ad load. - View adView = findViewById(R.id.adview); - - // Reload the ad. `getContext()` can be used instead of `getActivity.getApplicationContext()` once the minimum API >= 23. - AdHelper.loadAd(adView, getApplicationContext(), activity, getString(R.string.ad_unit_id)); - } + // Exit the full screen video. + exitFullScreenVideo(); } // Upload files. @@ -5864,7 +5846,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook String[] ultraListResults = blocklistHelper.checkBlocklist(currentDomain, url, isThirdPartyRequest, ultraList); // Process the UltraList results. - if (ultraListResults[0].equals(BlocklistHelper.REQUEST_BLOCKED)) { // The resource request matched UltraLists's blacklist. + if (ultraListResults[0].equals(BlocklistHelper.REQUEST_BLOCKED)) { // The resource request matched UltraList's blacklist. // Add the result to the resource requests. nestedScrollWebView.addResourceRequest(new String[] {ultraListResults[0], ultraListResults[1], ultraListResults[2], ultraListResults[3], ultraListResults[4], ultraListResults[5]}); @@ -6118,25 +6100,25 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { - // Get the preferences. - boolean scrollAppBar = sharedPreferences.getBoolean("scroll_app_bar", true); - - // Set the top padding of the swipe refresh layout according to the app bar scrolling preference. This can't be done in `appAppSettings()` because the app bar is not yet populated there. - if (scrollAppBar || (inFullScreenBrowsingMode && hideAppBar)) { - // No padding is needed because it will automatically be placed below the app bar layout due to the scrolling layout behavior. - swipeRefreshLayout.setPadding(0, 0, 0, 0); - - // The swipe to refresh circle doesn't always hide itself completely unless it is moved up 10 pixels. - swipeRefreshLayout.setProgressViewOffset(false, defaultProgressViewStartOffset - 10, defaultProgressViewEndOffset); - } else { - // Get the app bar layout height. This can't be done in `applyAppSettings()` because the app bar is not yet populated there. - appBarHeight = appBarLayout.getHeight(); - - // The swipe refresh layout must be manually moved below the app bar layout. - swipeRefreshLayout.setPadding(0, appBarHeight, 0, 0); - - // The swipe to refresh circle doesn't always hide itself completely unless it is moved up 10 pixels. - swipeRefreshLayout.setProgressViewOffset(false, defaultProgressViewStartOffset - 10 + appBarHeight, defaultProgressViewEndOffset + appBarHeight); + // Set the padding and layout settings if the app bar is at the top of the screen. + if (!bottomAppBar) { + // Set the top padding of the swipe refresh layout according to the app bar scrolling preference. This can't be done in `appAppSettings()` because the app bar is not yet populated there. + if (scrollAppBar || (inFullScreenBrowsingMode && hideAppBar)) { + // No padding is needed because it will automatically be placed below the app bar layout due to the scrolling layout behavior. + swipeRefreshLayout.setPadding(0, 0, 0, 0); + + // The swipe to refresh circle doesn't always hide itself completely unless it is moved up 10 pixels. + swipeRefreshLayout.setProgressViewOffset(false, defaultProgressViewStartOffset - 10, defaultProgressViewEndOffset); + } else { + // Get the app bar layout height. This can't be done in `applyAppSettings()` because the app bar is not yet populated there. + appBarHeight = appBarLayout.getHeight(); + + // The swipe refresh layout must be manually moved below the app bar layout. + swipeRefreshLayout.setPadding(0, appBarHeight, 0, 0); + + // The swipe to refresh circle doesn't always hide itself completely unless it is moved up 10 pixels. + swipeRefreshLayout.setProgressViewOffset(false, defaultProgressViewStartOffset - 10 + appBarHeight, defaultProgressViewEndOffset + appBarHeight); + } } // Reset the list of resource requests. @@ -6153,7 +6135,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Display the formatted URL text. urlEditText.setText(url); - // Apply text highlighting to `urlTextBox`. + // Apply text highlighting to the URL text box. highlightUrlText(); // Hide the keyboard. @@ -6195,7 +6177,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook @Override public void onPageFinished(WebView view, String url) { // Flush any cookies to persistent storage. The cookie manager has become very lazy about flushing cookies in recent versions. - if (nestedScrollWebView.getAcceptFirstPartyCookies() && Build.VERSION.SDK_INT >= 21) { + if (nestedScrollWebView.getAcceptCookies() && Build.VERSION.SDK_INT >= 21) { CookieManager.getInstance().flush(); }