From e0827590ff00dc4828c5a607d992b107994ff470 Mon Sep 17 00:00:00 2001 From: Soren Stoutner Date: Wed, 10 Apr 2019 17:02:49 -0700 Subject: [PATCH] Make swipe to refresh tab aware. --- .../activities/MainWebViewActivity.java | 250 +++++++++++------- .../fragments/SettingsFragment.java | 5 +- .../views/NestedScrollWebView.java | 37 ++- 3 files changed, 186 insertions(+), 106 deletions(-) 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 c0f2b824..c8d6896b 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java @@ -222,26 +222,15 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `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; // TODO ? // `searchURL` is used in `loadURLFromTextBox()` and `applyProxyThroughOrbot()`. 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; // TODO. Create it from `optionsMenu`. - // TODO. This could probably be removed. // The blocklist helper is used in `onCreate()` and `WebViewPagerAdapter`. private BlockListHelper blockListHelper; @@ -307,9 +296,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `privateDataDirectoryString` is used in `onCreate()`, `onOptionsItemSelected()`, and `onNavigationItemSelected()`. private String privateDataDirectoryString; // TODO. - // `findOnPageEditText` is used in `onCreate()`, `onOptionsItemSelected()`, and `closeFindOnPage()`. - private EditText findOnPageEditText; // TODO. - // `displayAdditionalAppBarIcons` is used in `onCreate()` and `onCreateOptionsMenu()`. private boolean displayAdditionalAppBarIcons; // TODO. @@ -342,9 +328,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `bookmarksListView` is used in `onCreate()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, and `loadBookmarksFolder()`. private ListView bookmarksListView; // TODO. - // `bookmarksTitleTextView` is used in `onCreate()` and `loadBookmarksFolder()`. - private TextView bookmarksTitleTextView; // TODO. - // `bookmarksCursor` is used in `onDestroy()`, `onOptionsItemSelected()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and `loadBookmarksFolder()`. private Cursor bookmarksCursor; @@ -365,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; @@ -508,11 +487,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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); + EditText findOnPageEditText = findViewById(R.id.find_on_page_edittext); // Listen for touches on the navigation menu. navigationView.setNavigationItemSelectedListener(this); @@ -841,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); @@ -856,10 +832,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Destroy the bare WebView. bareWebView.destroy(); - // 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(); @@ -1135,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. @@ -1147,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); @@ -1164,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")); @@ -1204,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); @@ -1246,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)); @@ -1266,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()); @@ -1306,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. @@ -1587,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(); @@ -1930,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: @@ -1952,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)); @@ -1977,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); @@ -3296,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); @@ -3410,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(); @@ -3442,7 +3424,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Reset the favorite icon if specified. if (resetFavoriteIcon) { // Initialize the favorite icon. - currentWebView.initializeFavoriteIcon(); + nestedScrollWebView.initializeFavoriteIcon(); // Get a handle for the tab layout. TabLayout tabLayout = findViewById(R.id.tablayout); @@ -3466,9 +3448,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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); @@ -3530,12 +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); @@ -3546,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, @@ -3605,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; } @@ -3621,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 { @@ -3634,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. @@ -3659,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) { @@ -3704,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); } @@ -3747,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 { @@ -3767,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. @@ -3872,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 { @@ -3912,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 { @@ -4126,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); @@ -4190,6 +4200,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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); @@ -4203,6 +4217,20 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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); @@ -4421,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. @@ -4462,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); } @@ -5025,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); } @@ -5066,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); @@ -5097,8 +5138,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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); @@ -5141,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(); @@ -5149,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. diff --git a/app/src/main/java/com/stoutner/privacybrowser/fragments/SettingsFragment.java b/app/src/main/java/com/stoutner/privacybrowser/fragments/SettingsFragment.java index b9f8e16c..410a21ed 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/fragments/SettingsFragment.java +++ b/app/src/main/java/com/stoutner/privacybrowser/fragments/SettingsFragment.java @@ -819,6 +819,7 @@ public class SettingsFragment extends PreferenceFragment { saveFormDataPreference.setIcon(R.drawable.form_data_disabled_light); } } + break; case "user_agent": // Get the new user agent name. @@ -940,7 +941,7 @@ public class SettingsFragment extends PreferenceFragment { // Assert that the intent is not null to remove the lint error below. assert allowScreenshotsRestartIntent != null; - // `Intent.FLAG_ACTIVITY_CLEAR_TASK` removes all activities from the stack. It requires `Intent.FLAG_ACTIVITY_NEW_TASK`. + // `Intent.FLAG_ACTIVITY_CLEAR_TASK` removes all activities from the stack. It requires `Intent.FLAG_ACTIVITY_NEW_TASK`. TODO. allowScreenshotsRestartIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); // Make it so. @@ -1480,7 +1481,7 @@ public class SettingsFragment extends PreferenceFragment { // Assert that the intent is not null to remove the lint error below. assert changeThemeRestartIntent != null; - // `Intent.FLAG_ACTIVITY_CLEAR_TASK` removes all activities from the stack. It requires `Intent.FLAG_ACTIVITY_NEW_TASK`. + // `Intent.FLAG_ACTIVITY_CLEAR_TASK` removes all activities from the stack. It requires `Intent.FLAG_ACTIVITY_NEW_TASK`. TODO. changeThemeRestartIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); // Make it so. diff --git a/app/src/main/java/com/stoutner/privacybrowser/views/NestedScrollWebView.java b/app/src/main/java/com/stoutner/privacybrowser/views/NestedScrollWebView.java index ca93d6fb..6cedf5c2 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/views/NestedScrollWebView.java +++ b/app/src/main/java/com/stoutner/privacybrowser/views/NestedScrollWebView.java @@ -27,14 +27,14 @@ import android.util.AttributeSet; import android.view.MotionEvent; import android.webkit.WebView; -import com.stoutner.privacybrowser.R; - import androidx.annotation.NonNull; import androidx.core.content.ContextCompat; import androidx.core.view.NestedScrollingChild2; import androidx.core.view.NestedScrollingChildHelper; import androidx.core.view.ViewCompat; +import com.stoutner.privacybrowser.R; + import java.util.ArrayList; import java.util.Date; @@ -100,6 +100,12 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild // The default or favorite icon. Bitmap favoriteOrDefaultIcon; + // Track night mode. + private boolean nightMode; + + // Track swipe to refresh. + private boolean swipeToRefresh; + // The nested scrolling child helper is used throughout the class. private NestedScrollingChildHelper nestedScrollingChildHelper; @@ -519,6 +525,30 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild } + // Night mode. + public void setNightMode(boolean status) { + // Store the night mode status. + nightMode = status; + } + + public boolean getNightMode() { + // Return the night mode status. + return nightMode; + } + + + // Swipe to refresh. + public void setSwipeToRefresh(boolean status) { + // Store the swipe to refresh status. + swipeToRefresh = status; + } + + public boolean getSwipeToRefresh() { + // Return the swipe to refresh status. + return swipeToRefresh; + } + + @Override public boolean onTouchEvent(MotionEvent motionEvent) { @@ -560,12 +590,13 @@ public class NestedScrollWebView extends WebView implements NestedScrollingChild // Dispatch the nested pre-school. This scrolls the app bar if it needs it. `offsetInWindow` will be returned with an updated value. if (dispatchNestedPreScroll(0, preScrollDeltaY, consumedScroll, offsetInWindow)) { // Update the scroll delta Y if some of it was consumed. + // There is currently a bug in Android where if scrolling up at a certain slow speed the input can lock the pre scroll and continue to consume it after the app bar is fully displayed. scrollDeltaY = preScrollDeltaY - consumedScroll[1]; } // Check to see if the WebView is at the top and and the scroll action is downward. if ((webViewYPosition == 0) && (scrollDeltaY < 0)) { // Swipe to refresh is being engaged. - // Stop the nested scroll so that swipe to refresh has complete control. + // Stop the nested scroll so that swipe to refresh has complete control. This way releasing the scroll to refresh circle doesn't scroll the WebView at the same time. stopNestedScroll(); } else { // Swipe to refresh is not being engaged. // Start the nested scroll so that the app bar can scroll off the screen. -- 2.45.2