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=14a2264eb60492d4b4e8b68c9573800de0553fdd;hp=be28dd88489c516c2d48549f37f7db1b7d07b011;hb=1a3d457d10c7b6c64d2454834bb0794909e43bd9;hpb=9d621a09cdc72a3ad434084b3aab297f3a7a9d44 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 be28dd88..14a2264e 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java @@ -102,6 +102,7 @@ import androidx.appcompat.widget.Toolbar; import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; +import androidx.core.content.res.ResourcesCompat; import androidx.core.view.GravityCompat; import androidx.drawerlayout.widget.DrawerLayout; import androidx.fragment.app.DialogFragment; @@ -168,12 +169,17 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; public class MainWebViewActivity extends AppCompatActivity implements CreateBookmarkDialog.CreateBookmarkListener, CreateBookmarkFolderDialog.CreateBookmarkFolderListener, EditBookmarkFolderDialog.EditBookmarkFolderListener, FontSizeDialog.UpdateFontSizeListener, NavigationView.OnNavigationItemSelectedListener, OpenDialog.OpenListener, PinnedMismatchDialog.PinnedMismatchListener, PopulateBlocklists.PopulateBlocklistsListener, SaveDialog.SaveWebpageListener, StoragePermissionDialog.StoragePermissionDialogListener, UrlHistoryDialog.NavigateHistoryListener, WebViewTabFragment.NewTabListener { + // The executor service handles background tasks. It is accessed from `ViewSourceActivity`. TODO. Change the number of threads, or create a single thread executor. + public static ExecutorService executorService = Executors.newFixedThreadPool(4); + // `orbotStatus` is public static so it can be accessed from `OrbotProxyHelper`. It is also used in `onCreate()`, `onResume()`, and `applyProxy()`. public static String orbotStatus = "unknown"; @@ -216,6 +222,23 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook private final int PERMISSION_SAVE_AS_ARCHIVE_REQUEST_CODE = 2; private final int PERMISSION_SAVE_AS_IMAGE_REQUEST_CODE = 3; + // Define the saved instance state constants. + private final String SAVED_STATE_ARRAY_LIST = "saved_state_array_list"; + private final String SAVED_NESTED_SCROLL_WEBVIEW_STATE_ARRAY_LIST = "saved_nested_scroll_webview_state_array_list"; + private final String SAVED_TAB_POSITION = "saved_tab_position"; + private final String PROXY_MODE = "proxy_mode"; + + // Define the saved instance state variables. + private ArrayList savedStateArrayList; + private ArrayList savedNestedScrollWebViewStateArrayList; + private int savedTabPosition; + private String savedProxyMode; + + // Define the class views. + private AppBarLayout appBarLayout; + private TabLayout tabLayout; + private ViewPager webViewPager; + // The current WebView is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onRestart()`, `onCreateContextMenu()`, `findPreviousOnPage()`, // `findNextOnPage()`, `closeFindOnPage()`, `loadUrlFromTextBox()`, `onSslMismatchBack()`, `applyProxy()`, and `applyDomainSettings()`. private NestedScrollWebView currentWebView; @@ -317,6 +340,15 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Run the default commands. super.onCreate(savedInstanceState); + // Check to see if the activity has been restarted. + if (savedInstanceState != null) { + // Store the saved instance state variables. + savedStateArrayList = savedInstanceState.getParcelableArrayList(SAVED_STATE_ARRAY_LIST); + savedNestedScrollWebViewStateArrayList = savedInstanceState.getParcelableArrayList(SAVED_NESTED_SCROLL_WEBVIEW_STATE_ARRAY_LIST); + savedTabPosition = savedInstanceState.getInt(SAVED_TAB_POSITION); + savedProxyMode = savedInstanceState.getString(PROXY_MODE); + } + // Initialize the default preference values the first time the program is run. `false` keeps this command from resetting any current preferences back to default. PreferenceManager.setDefaultValues(this, R.xml.preferences, false); @@ -363,10 +395,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Set the content view. setContentView(R.layout.main_framelayout); - // Get handles for the views that need to be modified. + // Get handles for the views. DrawerLayout drawerLayout = findViewById(R.id.drawerlayout); + appBarLayout = findViewById(R.id.appbar_layout); Toolbar toolbar = findViewById(R.id.toolbar); - ViewPager webViewPager = findViewById(R.id.webviewpager); + tabLayout = findViewById(R.id.tablayout); + webViewPager = findViewById(R.id.webviewpager); // Get a handle for the app compat delegate. AppCompatDelegate appCompatDelegate = getDelegate(); @@ -399,6 +433,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Store up to 100 tabs in memory. webViewPager.setOffscreenPageLimit(100); + // Initialize the app. + initializeApp(); + + // Apply the app settings from the shared preferences. + applyAppSettings(); + // Populate the blocklists. new PopulateBlocklists(this, this).execute(); } @@ -411,8 +451,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Replace the intent that started the app with this one. setIntent(intent); - // Process the intent here if Privacy Browser is fully initialized. If the process has been killed by the system while sitting in the background, this will be handled in `initializeWebView()`. - if (ultraPrivacy != null) { + // Check to see if the app is being restarted. + if (savedStateArrayList == null || savedStateArrayList.size() == 0) { // The activity is running for the first time. // Get the information from the intent. String intentAction = intent.getAction(); Uri intentUriData = intent.getData(); @@ -429,7 +469,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook String url; // If the intent action is a web search, perform the search. - if (isWebSearch) { + if (isWebSearch) { // The intent is a web search. // Create an encoded URL string. String encodedUrlString; @@ -629,6 +669,50 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } } + @Override + public void onSaveInstanceState(@NonNull Bundle savedInstanceState) { + // Run the default commands. + super.onSaveInstanceState(savedInstanceState); + + // Create the saved state array lists. + ArrayList savedStateArrayList = new ArrayList<>(); + ArrayList savedNestedScrollWebViewStateArrayList = new ArrayList<>(); + + // Get the URLs from each tab. + 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(); + + if (fragmentView != null) { + // Get the nested scroll WebView from the tab fragment. + NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview); + + // Create saved state bundle. + Bundle savedStateBundle = new Bundle(); + + // Get the current states. + nestedScrollWebView.saveState(savedStateBundle); + Bundle savedNestedScrollWebViewStateBundle = nestedScrollWebView.saveNestedScrollWebViewState(); + + // Store the saved states in the array lists. + savedStateArrayList.add(savedStateBundle); + savedNestedScrollWebViewStateArrayList.add(savedNestedScrollWebViewStateBundle); + } + } + + // Get the current tab position. + int currentTabPosition = tabLayout.getSelectedTabPosition(); + + // Store the saved states in the bundle. + savedInstanceState.putParcelableArrayList(SAVED_STATE_ARRAY_LIST, savedStateArrayList); + savedInstanceState.putParcelableArrayList(SAVED_NESTED_SCROLL_WEBVIEW_STATE_ARRAY_LIST, savedNestedScrollWebViewStateArrayList); + savedInstanceState.putInt(SAVED_TAB_POSITION, currentTabPosition); + savedInstanceState.putString(PROXY_MODE, proxyMode); + } + @Override public void onDestroy() { // Unregister the orbot status broadcast receiver if it exists. @@ -668,6 +752,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook 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. MenuItem refreshMenuItem = menu.findItem(R.id.refresh); + MenuItem darkWebViewMenuItem = menu.findItem(R.id.dark_webview); MenuItem adConsentMenuItem = menu.findItem(R.id.ad_consent); // Only display third-party cookies if API >= 21 @@ -680,6 +765,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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 display the dark WebView menu item if API >= 21. + darkWebViewMenuItem.setVisible(Build.VERSION.SDK_INT >= 21); + // Only show Ad Consent if this is the free flavor. adConsentMenuItem.setVisible(BuildConfig.FLAVOR.contentEquals("free")); @@ -711,12 +799,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; // Set the icon according to the current theme status. - if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) { - // Set the dark stop icon. - refreshMenuItem.setIcon(R.drawable.close_night); - } else { - // Set the light stop icon. + if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { refreshMenuItem.setIcon(R.drawable.close_day); + } else { + refreshMenuItem.setIcon(R.drawable.close_night); } } } @@ -751,7 +837,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook MenuItem swipeToRefreshMenuItem = menu.findItem(R.id.swipe_to_refresh); MenuItem wideViewportMenuItem = menu.findItem(R.id.wide_viewport); MenuItem displayImagesMenuItem = menu.findItem(R.id.display_images); - MenuItem nightModeMenuItem = menu.findItem(R.id.night_mode); + MenuItem darkWebViewMenuItem = menu.findItem(R.id.dark_webview); // Get a handle for the cookie manager. CookieManager cookieManager = CookieManager.getInstance(); @@ -788,7 +874,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook swipeToRefreshMenuItem.setChecked(currentWebView.getSwipeToRefresh()); wideViewportMenuItem.setChecked(currentWebView.getSettings().getUseWideViewPort()); 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)); @@ -811,6 +896,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Enable DOM Storage if JavaScript is enabled. domStorageMenuItem.setEnabled(currentWebView.getSettings().getJavaScriptEnabled()); + + // Set the checkbox status for dark WebView if the WebView supports it. + if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) { + darkWebViewMenuItem.setChecked(WebSettingsCompat.getForceDark(currentWebView.getSettings()) == WebSettingsCompat.FORCE_DARK_ON); + } } // Set the checked status of the first party cookies menu item. @@ -1006,11 +1096,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Display a `Snackbar`. if (currentWebView.getSettings().getJavaScriptEnabled()) { // JavaScrip is enabled. - Snackbar.make(findViewById(R.id.webviewpager), R.string.javascript_enabled, Snackbar.LENGTH_SHORT).show(); + Snackbar.make(webViewPager, R.string.javascript_enabled, Snackbar.LENGTH_SHORT).show(); } else if (cookieManager.acceptCookie()) { // JavaScript is disabled, but first-party cookies are enabled. - Snackbar.make(findViewById(R.id.webviewpager), R.string.javascript_disabled, Snackbar.LENGTH_SHORT).show(); + Snackbar.make(webViewPager, R.string.javascript_disabled, Snackbar.LENGTH_SHORT).show(); } else { // Privacy mode. - Snackbar.make(findViewById(R.id.webviewpager), R.string.privacy_mode, Snackbar.LENGTH_SHORT).show(); + Snackbar.make(webViewPager, R.string.privacy_mode, Snackbar.LENGTH_SHORT).show(); } // Reload the current WebView. @@ -1056,11 +1146,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Display a snackbar. if (cookieManager.acceptCookie()) { // First-party cookies are enabled. - Snackbar.make(findViewById(R.id.webviewpager), R.string.first_party_cookies_enabled, Snackbar.LENGTH_SHORT).show(); + Snackbar.make(webViewPager, R.string.first_party_cookies_enabled, Snackbar.LENGTH_SHORT).show(); } else if (currentWebView.getSettings().getJavaScriptEnabled()) { // JavaScript is still enabled. - Snackbar.make(findViewById(R.id.webviewpager), R.string.first_party_cookies_disabled, Snackbar.LENGTH_SHORT).show(); + Snackbar.make(webViewPager, R.string.first_party_cookies_disabled, Snackbar.LENGTH_SHORT).show(); } else { // Privacy mode. - Snackbar.make(findViewById(R.id.webviewpager), R.string.privacy_mode, Snackbar.LENGTH_SHORT).show(); + Snackbar.make(webViewPager, R.string.privacy_mode, Snackbar.LENGTH_SHORT).show(); } // Reload the current WebView. @@ -1079,9 +1169,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Display a snackbar. if (cookieManager.acceptThirdPartyCookies(currentWebView)) { - Snackbar.make(findViewById(R.id.webviewpager), R.string.third_party_cookies_enabled, Snackbar.LENGTH_SHORT).show(); + Snackbar.make(webViewPager, R.string.third_party_cookies_enabled, Snackbar.LENGTH_SHORT).show(); } else { - Snackbar.make(findViewById(R.id.webviewpager), R.string.third_party_cookies_disabled, Snackbar.LENGTH_SHORT).show(); + Snackbar.make(webViewPager, R.string.third_party_cookies_disabled, Snackbar.LENGTH_SHORT).show(); } // Reload the current WebView. @@ -1103,9 +1193,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Display a snackbar. if (currentWebView.getSettings().getDomStorageEnabled()) { - Snackbar.make(findViewById(R.id.webviewpager), R.string.dom_storage_enabled, Snackbar.LENGTH_SHORT).show(); + Snackbar.make(webViewPager, R.string.dom_storage_enabled, Snackbar.LENGTH_SHORT).show(); } else { - Snackbar.make(findViewById(R.id.webviewpager), R.string.dom_storage_disabled, Snackbar.LENGTH_SHORT).show(); + Snackbar.make(webViewPager, R.string.dom_storage_disabled, Snackbar.LENGTH_SHORT).show(); } // Reload the current WebView. @@ -1124,9 +1214,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Display a snackbar. if (currentWebView.getSettings().getSaveFormData()) { - Snackbar.make(findViewById(R.id.webviewpager), R.string.form_data_enabled, Snackbar.LENGTH_SHORT).show(); + Snackbar.make(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(); + Snackbar.make(webViewPager, R.string.form_data_disabled, Snackbar.LENGTH_SHORT).show(); } // Update the privacy icon. `true` runs `invalidateOptionsMenu` as the last step. @@ -1139,7 +1229,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook return true; case R.id.clear_cookies: - Snackbar.make(findViewById(R.id.webviewpager), R.string.cookies_deleted, Snackbar.LENGTH_LONG) + Snackbar.make(webViewPager, R.string.cookies_deleted, Snackbar.LENGTH_LONG) .setAction(R.string.undo, v -> { // Do nothing because everything will be handled by `onDismissed()` below. }) @@ -1163,7 +1253,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook return true; case R.id.clear_dom_storage: - Snackbar.make(findViewById(R.id.webviewpager), R.string.dom_storage_deleted, Snackbar.LENGTH_LONG) + Snackbar.make(webViewPager, R.string.dom_storage_deleted, Snackbar.LENGTH_LONG) .setAction(R.string.undo, v -> { // Do nothing because everything will be handled by `onDismissed()` below. }) @@ -1221,7 +1311,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Form data can be remove once the minimum API >= 26. case R.id.clear_form_data: - Snackbar.make(findViewById(R.id.webviewpager), R.string.form_data_deleted, Snackbar.LENGTH_LONG) + Snackbar.make(webViewPager, R.string.form_data_deleted, Snackbar.LENGTH_LONG) .setAction(R.string.undo, v -> { // Do nothing because everything will be handled by `onDismissed()` below. }) @@ -1557,28 +1647,19 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Consume the event. return true; - case R.id.night_mode: - // Toggle night mode. - currentWebView.setNightMode(!currentWebView.getNightMode()); - - // Enable or disable JavaScript according to night mode, the global preference, and any domain settings. - 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(currentWebView.getDomainSettingsJavaScriptEnabled()); - } 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)); + case R.id.dark_webview: + // Check to see if dark WebView is supported by this WebView. + if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) { + // Toggle the dark WebView setting. + if (WebSettingsCompat.getForceDark(currentWebView.getSettings()) == WebSettingsCompat.FORCE_DARK_ON) { // Dark WebView is currently enabled. + // Turn off dark WebView. + WebSettingsCompat.setForceDark(currentWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF); + } else { // Dark WebView is currently disabled. + // turn on dark WebView. + WebSettingsCompat.setForceDark(currentWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON); + } } - // Update the privacy icons. - updatePrivacyIcons(false); - - // Reload the website. - currentWebView.reload(); - // Consume the event. return true; @@ -2561,9 +2642,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Override `onBackPressed` to handle the navigation drawer and and the WebViews. @Override public void onBackPressed() { - // Get a handle for the drawer layout and the tab layout. + // Get a handle for the drawer layout. DrawerLayout drawerLayout = findViewById(R.id.drawerlayout); - TabLayout tabLayout = findViewById(R.id.tablayout); if (drawerLayout.isDrawerVisible(GravityCompat.START)) { // The navigation drawer is open. // Close the navigation drawer. @@ -3131,160 +3211,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } } - private void applyAppSettings() { - // Initialize the app if this is the first run. This is done here instead of in `onCreate()` to shorten the time that an unthemed background is displayed on app startup. - if (webViewDefaultUserAgent == null) { - initializeApp(); - } - - // Get a handle for the shared preferences. - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); - - // Store the values from the shared preferences in variables. - incognitoModeEnabled = sharedPreferences.getBoolean("incognito_mode", false); - boolean doNotTrackEnabled = sharedPreferences.getBoolean("do_not_track", false); - sanitizeGoogleAnalytics = sharedPreferences.getBoolean("google_analytics", true); - sanitizeFacebookClickIds = sharedPreferences.getBoolean("facebook_click_ids", true); - sanitizeTwitterAmpRedirects = sharedPreferences.getBoolean("twitter_amp_redirects", true); - proxyMode = sharedPreferences.getString("proxy", getString(R.string.proxy_default_value)); - fullScreenBrowsingModeEnabled = sharedPreferences.getBoolean("full_screen_browsing_mode", false); - hideAppBar = sharedPreferences.getBoolean("hide_app_bar", true); - scrollAppBar = sharedPreferences.getBoolean("scroll_app_bar", true); - - // Get the search string. - String searchString = sharedPreferences.getString("search", getString(R.string.search_default_value)); - - // Set the search string. - if (searchString.equals("Custom URL")) { // A custom search string is used. - searchURL = sharedPreferences.getString("search_custom_url", getString(R.string.search_custom_url_default_value)); - } else { // A custom search string is not used. - searchURL = searchString; - } - - // Get a handle for the app compat delegate. - AppCompatDelegate appCompatDelegate = getDelegate(); - - // Get handles for the views that need to be modified. - FrameLayout rootFrameLayout = findViewById(R.id.root_framelayout); - AppBarLayout appBarLayout = findViewById(R.id.appbar_layout); - ActionBar actionBar = appCompatDelegate.getSupportActionBar(); - Toolbar toolbar = findViewById(R.id.toolbar); - LinearLayout findOnPageLinearLayout = findViewById(R.id.find_on_page_linearlayout); - LinearLayout tabsLinearLayout = findViewById(R.id.tabs_linearlayout); - SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout); - - // Remove the incorrect lint warning below that the action bar might be null. - assert actionBar != null; - - // Apply the proxy. - applyProxy(false); - - // Set Do Not Track status. - if (doNotTrackEnabled) { - customHeaders.put("DNT", "1"); - } else { - customHeaders.remove("DNT"); - } - - // Get the current layout parameters. Using coordinator layout parameters allows the `setBehavior()` command and using app bar layout parameters allows the `setScrollFlags()` command. - CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams(); - AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) toolbar.getLayoutParams(); - AppBarLayout.LayoutParams findOnPageLayoutParams = (AppBarLayout.LayoutParams) findOnPageLinearLayout.getLayoutParams(); - AppBarLayout.LayoutParams tabsLayoutParams = (AppBarLayout.LayoutParams) tabsLinearLayout.getLayoutParams(); - - // Add the scrolling behavior to the layout parameters. - if (scrollAppBar) { - // Enable scrolling of the app bar. - swipeRefreshLayoutParams.setBehavior(new AppBarLayout.ScrollingViewBehavior()); - toolbarLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP); - findOnPageLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP); - tabsLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP); - } else { - // Disable scrolling of the app bar. - swipeRefreshLayoutParams.setBehavior(null); - toolbarLayoutParams.setScrollFlags(0); - findOnPageLayoutParams.setScrollFlags(0); - tabsLayoutParams.setScrollFlags(0); - - // Expand the app bar if it is currently collapsed. - appBarLayout.setExpanded(true); - } - - // Apply the modified layout parameters. - swipeRefreshLayout.setLayoutParams(swipeRefreshLayoutParams); - toolbar.setLayoutParams(toolbarLayoutParams); - findOnPageLinearLayout.setLayoutParams(findOnPageLayoutParams); - tabsLinearLayout.setLayoutParams(tabsLayoutParams); - - // Set the app bar scrolling for each WebView. - for (int i = 0; i < webViewPagerAdapter.getCount(); i++) { - // Get the WebView tab fragment. - WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i); - - // Get the fragment view. - View fragmentView = webViewTabFragment.getView(); - - // Only modify the WebViews if they exist. - if (fragmentView != null) { - // Get the nested scroll WebView from the tab fragment. - NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview); - - // Set the app bar scrolling. - nestedScrollWebView.setNestedScrollingEnabled(scrollAppBar); - } - } - - // Update the full screen browsing mode settings. - if (fullScreenBrowsingModeEnabled && inFullScreenBrowsingMode) { // Privacy Browser is currently in full screen browsing mode. - // Update the visibility of the app bar, which might have changed in the settings. - if (hideAppBar) { - // Hide the tab linear layout. - tabsLinearLayout.setVisibility(View.GONE); - - // Hide the action bar. - actionBar.hide(); - } else { - // Show the tab linear layout. - tabsLinearLayout.setVisibility(View.VISIBLE); - - // Show the action bar. - actionBar.show(); - } - - // Hide the banner ad in the free flavor. - if (BuildConfig.FLAVOR.contentEquals("free")) { - AdHelper.hideAd(findViewById(R.id.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 { // Privacy Browser is not in full screen browsing mode. - // Reset the full screen tracker, which could be true if Privacy Browser was in full screen mode before entering settings and full screen browsing was disabled. - inFullScreenBrowsingMode = false; - - // Show the tab linear layout. - tabsLinearLayout.setVisibility(View.VISIBLE); - - // Show the action bar. - actionBar.show(); - - // Show the banner ad in the free flavor. - if (BuildConfig.FLAVOR.contentEquals("free")) { - // Initialize the ads. If this isn't the first run, `loadAd()` will be automatically called instead. - AdHelper.initializeAds(findViewById(R.id.adview), getApplicationContext(), getSupportFragmentManager(), getString(R.string.google_app_id), getString(R.string.ad_unit_id)); - } - - // Remove the `SYSTEM_UI` flags from the root frame layout. - rootFrameLayout.setSystemUiVisibility(0); - } - } - private void initializeApp() { // Get a handle for the input method. InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); @@ -3292,11 +3218,20 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Remove the lint warning below that the input method manager might be null. assert inputMethodManager != null; - // Initialize the foreground color spans for highlighting the URLs. We have to use the deprecated `getColor()` until API >= 23. - redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700)); + // Initialize the gray foreground color spans for highlighting the URLs. The deprecated `getResources()` must be used until API >= 23. initialGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500)); finalGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500)); + // Get the current theme status. + int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; + + // Set the red color span according to the theme. + if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { + redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700)); + } else { + redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_900)); + } + // Get handles for the URL views. EditText urlEditText = findViewById(R.id.url_edittext); @@ -3390,9 +3325,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get handles for views that need to be modified. DrawerLayout drawerLayout = findViewById(R.id.drawerlayout); NavigationView navigationView = findViewById(R.id.navigationview); - TabLayout tabLayout = findViewById(R.id.tablayout); SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout); - ViewPager webViewPager = findViewById(R.id.webviewpager); 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); @@ -3402,12 +3335,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Listen for touches on the navigation menu. navigationView.setNavigationItemSelectedListener(this); - // Get handles for the navigation menu and the back and forward menu items. The menu is 0 based. + // Get handles for the navigation menu and the back and forward menu items. Menu navigationMenu = navigationView.getMenu(); - MenuItem navigationBackMenuItem = navigationMenu.getItem(2); - MenuItem navigationForwardMenuItem = navigationMenu.getItem(3); - MenuItem navigationHistoryMenuItem = navigationMenu.getItem(4); - MenuItem navigationRequestsMenuItem = navigationMenu.getItem(6); + MenuItem navigationBackMenuItem = navigationMenu.findItem(R.id.back); + MenuItem navigationForwardMenuItem = navigationMenu.findItem(R.id.forward); + MenuItem navigationHistoryMenuItem = navigationMenu.findItem(R.id.history); + MenuItem navigationRequestsMenuItem = navigationMenu.findItem(R.id.requests); // Update the web view pager every time a tab is modified. webViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @@ -3424,7 +3357,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Set the current WebView. setCurrentWebView(position); - // Select the corresponding tab if it does not match the currently selected page. This will happen if the page was scrolled via swiping in the view pager or by creating a new tab. + // Select the corresponding tab if it does not match the currently selected page. This will happen if the page was scrolled by creating a new tab. if (tabLayout.getSelectedTabPosition() != position) { // Create a handler to select the tab. Handler selectTabHandler = new Handler(); @@ -3562,14 +3495,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook defaultProgressViewStartOffset = swipeRefreshLayout.getProgressViewStartOffset(); defaultProgressViewEndOffset = swipeRefreshLayout.getProgressViewEndOffset(); - // Get the current theme status. - int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; - // Set the refresh color scheme according to the theme. - if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) { - swipeRefreshLayout.setColorSchemeResources(R.color.blue_500); - } else { + if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { swipeRefreshLayout.setColorSchemeResources(R.color.blue_700); + } else { + swipeRefreshLayout.setColorSchemeResources(R.color.violet_500); } // Initialize a color background typed value. @@ -3708,38 +3638,195 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook bareWebView.destroy(); } - @Override - public void navigateHistory(String url, int steps) { - // Apply the domain settings. - applyDomainSettings(currentWebView, url, false, false); - - // Load the history entry. - currentWebView.goBackOrForward(steps); - } + private void applyAppSettings() { + // Get a handle for the shared preferences. + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); - @Override - public void pinnedErrorGoBack() { - // Get the current web back forward list. - WebBackForwardList webBackForwardList = currentWebView.copyBackForwardList(); + // Store the values from the shared preferences in variables. + incognitoModeEnabled = sharedPreferences.getBoolean("incognito_mode", false); + boolean doNotTrackEnabled = sharedPreferences.getBoolean("do_not_track", false); + sanitizeGoogleAnalytics = sharedPreferences.getBoolean("google_analytics", true); + sanitizeFacebookClickIds = sharedPreferences.getBoolean("facebook_click_ids", true); + sanitizeTwitterAmpRedirects = sharedPreferences.getBoolean("twitter_amp_redirects", true); + proxyMode = sharedPreferences.getString("proxy", getString(R.string.proxy_default_value)); + fullScreenBrowsingModeEnabled = sharedPreferences.getBoolean("full_screen_browsing_mode", false); + hideAppBar = sharedPreferences.getBoolean("hide_app_bar", true); + scrollAppBar = sharedPreferences.getBoolean("scroll_app_bar", true); - // Get the previous entry URL. - String previousUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() - 1).getUrl(); + // Apply the saved proxy mode if the app has been restarted. + if (savedProxyMode != null) { + // Apply the saved proxy mode. + proxyMode = savedProxyMode; - // Apply the domain settings. - applyDomainSettings(currentWebView, previousUrl, false, false); + // Reset the saved proxy mode. + savedProxyMode = null; + } - // Go back. - currentWebView.goBack(); - } + // Get the search string. + String searchString = sharedPreferences.getString("search", getString(R.string.search_default_value)); - // `reloadWebsite` is used if returning from the Domains activity. Otherwise JavaScript might not function correctly if it is newly enabled. - @SuppressLint("SetJavaScriptEnabled") - private void applyDomainSettings(NestedScrollWebView nestedScrollWebView, String url, boolean resetTab, boolean reloadWebsite) { - // Store the current URL. - nestedScrollWebView.setCurrentUrl(url); + // Set the search string. + if (searchString.equals("Custom URL")) { // A custom search string is used. + searchURL = sharedPreferences.getString("search_custom_url", getString(R.string.search_custom_url_default_value)); + } else { // A custom search string is not used. + searchURL = searchString; + } - // Parse the URL into a URI. - Uri uri = Uri.parse(url); + // Get a handle for the app compat delegate. + AppCompatDelegate appCompatDelegate = getDelegate(); + + // Get handles for the views that need to be modified. + FrameLayout rootFrameLayout = findViewById(R.id.root_framelayout); + ActionBar actionBar = appCompatDelegate.getSupportActionBar(); + Toolbar toolbar = findViewById(R.id.toolbar); + LinearLayout findOnPageLinearLayout = findViewById(R.id.find_on_page_linearlayout); + LinearLayout tabsLinearLayout = findViewById(R.id.tabs_linearlayout); + SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout); + + // Remove the incorrect lint warning below that the action bar might be null. + assert actionBar != null; + + // Apply the proxy. + applyProxy(false); + + // Set Do Not Track status. + if (doNotTrackEnabled) { + customHeaders.put("DNT", "1"); + } else { + customHeaders.remove("DNT"); + } + + // Get the current layout parameters. Using coordinator layout parameters allows the `setBehavior()` command and using app bar layout parameters allows the `setScrollFlags()` command. + CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams(); + AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) toolbar.getLayoutParams(); + AppBarLayout.LayoutParams findOnPageLayoutParams = (AppBarLayout.LayoutParams) findOnPageLinearLayout.getLayoutParams(); + AppBarLayout.LayoutParams tabsLayoutParams = (AppBarLayout.LayoutParams) tabsLinearLayout.getLayoutParams(); + + // Add the scrolling behavior to the layout parameters. + if (scrollAppBar) { + // Enable scrolling of the app bar. + swipeRefreshLayoutParams.setBehavior(new AppBarLayout.ScrollingViewBehavior()); + toolbarLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP); + findOnPageLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP); + tabsLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP); + } else { + // Disable scrolling of the app bar. + swipeRefreshLayoutParams.setBehavior(null); + toolbarLayoutParams.setScrollFlags(0); + findOnPageLayoutParams.setScrollFlags(0); + tabsLayoutParams.setScrollFlags(0); + + // Expand the app bar if it is currently collapsed. + appBarLayout.setExpanded(true); + } + + // Apply the modified layout parameters. + swipeRefreshLayout.setLayoutParams(swipeRefreshLayoutParams); + toolbar.setLayoutParams(toolbarLayoutParams); + findOnPageLinearLayout.setLayoutParams(findOnPageLayoutParams); + tabsLinearLayout.setLayoutParams(tabsLayoutParams); + + // Set the app bar scrolling for each WebView. + for (int i = 0; i < webViewPagerAdapter.getCount(); i++) { + // Get the WebView tab fragment. + WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i); + + // Get the fragment view. + View fragmentView = webViewTabFragment.getView(); + + // Only modify the WebViews if they exist. + if (fragmentView != null) { + // Get the nested scroll WebView from the tab fragment. + NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview); + + // Set the app bar scrolling. + nestedScrollWebView.setNestedScrollingEnabled(scrollAppBar); + } + } + + // Update the full screen browsing mode settings. + if (fullScreenBrowsingModeEnabled && inFullScreenBrowsingMode) { // Privacy Browser is currently in full screen browsing mode. + // Update the visibility of the app bar, which might have changed in the settings. + if (hideAppBar) { + // Hide the tab linear layout. + tabsLinearLayout.setVisibility(View.GONE); + + // Hide the action bar. + actionBar.hide(); + } else { + // Show the tab linear layout. + tabsLinearLayout.setVisibility(View.VISIBLE); + + // Show the action bar. + actionBar.show(); + } + + // Hide the banner ad in the free flavor. + if (BuildConfig.FLAVOR.contentEquals("free")) { + AdHelper.hideAd(findViewById(R.id.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 { // Privacy Browser is not in full screen browsing mode. + // Reset the full screen tracker, which could be true if Privacy Browser was in full screen mode before entering settings and full screen browsing was disabled. + inFullScreenBrowsingMode = false; + + // Show the tab linear layout. + tabsLinearLayout.setVisibility(View.VISIBLE); + + // Show the action bar. + actionBar.show(); + + // Show the banner ad in the free flavor. + if (BuildConfig.FLAVOR.contentEquals("free")) { + // Initialize the ads. If this isn't the first run, `loadAd()` will be automatically called instead. + AdHelper.initializeAds(findViewById(R.id.adview), getApplicationContext(), getSupportFragmentManager(), getString(R.string.google_app_id), getString(R.string.ad_unit_id)); + } + + // Remove the `SYSTEM_UI` flags from the root frame layout. + rootFrameLayout.setSystemUiVisibility(0); + } + } + + @Override + public void navigateHistory(String url, int steps) { + // Apply the domain settings. + applyDomainSettings(currentWebView, url, false, false); + + // Load the history entry. + currentWebView.goBackOrForward(steps); + } + + @Override + public void pinnedErrorGoBack() { + // Get the current web back forward list. + WebBackForwardList webBackForwardList = currentWebView.copyBackForwardList(); + + // Get the previous entry URL. + String previousUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() - 1).getUrl(); + + // Apply the domain settings. + applyDomainSettings(currentWebView, previousUrl, false, false); + + // Go back. + currentWebView.goBack(); + } + + // `reloadWebsite` is used if returning from the Domains activity. Otherwise JavaScript might not function correctly if it is newly enabled. + @SuppressLint("SetJavaScriptEnabled") + private void applyDomainSettings(NestedScrollWebView nestedScrollWebView, String url, boolean resetTab, boolean reloadWebsite) { + // Store the current URL. + nestedScrollWebView.setCurrentUrl(url); + + // Parse the URL into a URI. + Uri uri = Uri.parse(url); // Extract the domain from `uri`. String newHostName = uri.getHost(); @@ -3769,9 +3856,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get the current page position. int currentPagePosition = webViewPagerAdapter.getPositionForId(nestedScrollWebView.getWebViewFragmentId()); - // Get a handle for the tab layout. - TabLayout tabLayout = findViewById(R.id.tablayout); - // Get the corresponding tab. TabLayout.Tab tab = tabLayout.getTabAt(currentPagePosition); @@ -3877,7 +3961,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get the settings from the cursor. nestedScrollWebView.setDomainSettingsDatabaseId(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper._ID))); - nestedScrollWebView.setDomainSettingsJavaScriptEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_JAVASCRIPT)) == 1); + 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.getSettings().setDomStorageEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_DOM_STORAGE)) == 1); @@ -3899,7 +3983,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook String userAgentName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.USER_AGENT)); int fontSize = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.FONT_SIZE)); int swipeToRefreshInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SWIPE_TO_REFRESH)); - int nightModeInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.NIGHT_MODE)); + int webViewThemeInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.WEBVIEW_THEME)); int wideViewportInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.WIDE_VIEWPORT)); int displayWebpageImagesInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.DISPLAY_IMAGES)); boolean pinnedSslCertificate = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE)) == 1); @@ -3912,24 +3996,31 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook boolean pinnedIpAddresses = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_IP_ADDRESSES)) == 1); String pinnedHostIpAddresses = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.IP_ADDRESSES)); - // Create the pinned SSL date variables. + // Get the pinned SSL date longs. + long pinnedSslStartDateLong = currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE)); + long pinnedSslEndDateLong = currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE)); + + // Define the pinned SSL date variables. Date pinnedSslStartDate; Date pinnedSslEndDate; - // Set the pinned SSL certificate start date to `null` if the saved date `long` is 0 because creating a new Date results in an error if the input is 0. - if (currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE)) == 0) { + // Set the pinned SSL certificate start date to `null` if the saved date long is 0 because creating a new date results in an error if the input is 0. + if (pinnedSslStartDateLong == 0) { pinnedSslStartDate = null; } else { - pinnedSslStartDate = new Date(currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE))); + pinnedSslStartDate = new Date(pinnedSslStartDateLong); } - // Set the pinned SSL certificate end date to `null` if the saved date `long` is 0 because creating a new Date results in an error if the input is 0. - if (currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE)) == 0) { + // Set the pinned SSL certificate end date to `null` if the saved date long is 0 because creating a new date results in an error if the input is 0. + if (pinnedSslEndDateLong == 0) { pinnedSslEndDate = null; } else { - pinnedSslEndDate = new Date(currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE))); + pinnedSslEndDate = new Date(pinnedSslEndDateLong); } + // Close the current host domain settings cursor. + currentDomainSettingsCursor.close(); + // If there is a pinned SSL certificate, store it in the WebView. if (pinnedSslCertificate) { nestedScrollWebView.setPinnedSslCertificate(pinnedSslIssuedToCName, pinnedSslIssuedToOName, pinnedSslIssuedToUName, pinnedSslIssuedByCName, pinnedSslIssuedByOName, pinnedSslIssuedByUName, @@ -3941,37 +4032,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook nestedScrollWebView.setPinnedIpAddresses(pinnedHostIpAddresses); } - // Set night mode according to the night mode int. - switch (nightModeInt) { - case DomainsDatabaseHelper.SYSTEM_DEFAULT: - // Set night mode according to the current default. - nestedScrollWebView.setNightMode(sharedPreferences.getBoolean("night_mode", false)); - break; - - case DomainsDatabaseHelper.ENABLED: - // Enable night mode. - nestedScrollWebView.setNightMode(true); - break; - - case DomainsDatabaseHelper.DISABLED: - // Disable night mode. - nestedScrollWebView.setNightMode(false); - break; - } - - // Enable JavaScript if night mode is enabled. - if (nestedScrollWebView.getNightMode()) { - // Enable JavaScript. - nestedScrollWebView.getSettings().setJavaScriptEnabled(true); - } else { - // Set JavaScript according to the domain settings. - nestedScrollWebView.getSettings().setJavaScriptEnabled(nestedScrollWebView.getDomainSettingsJavaScriptEnabled()); - } - - // Close the current host domain settings cursor. - currentDomainSettingsCursor.close(); - - // Apply the domain settings. + // Apply the cookie domain settings. cookieManager.setAcceptCookie(nestedScrollWebView.getAcceptFirstPartyCookies()); // Set third-party cookies status if API >= 21. @@ -4075,6 +4136,36 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook swipeRefreshLayout.setEnabled(false); } + // Check to see if WebView themes are supported. + if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) { + // Set the WebView theme. + switch (webViewThemeInt) { + case DomainsDatabaseHelper.SYSTEM_DEFAULT: + // // Ge the current system theme status. + int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; + + // Set the WebView theme according to the current system theme status. + if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { // The system is in day mode. + // Turn off the WebView dark mode. + WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF); + } else { // The system is in night mode. + // Turn on the WebView dark mode. + WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON); + } + break; + + case DomainsDatabaseHelper.LIGHT_THEME: + // Turn off the WebView dark mode. + WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF); + break; + + case DomainsDatabaseHelper.DARK_THEME: + // Turn on the WebView dark mode. + WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON); + break; + } + } + // Set the viewport. switch (wideViewportInt) { case DomainsDatabaseHelper.SYSTEM_DEFAULT: @@ -4108,15 +4199,15 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get the current theme status. int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; - // Set a background on the URL relative layout to indicate that custom domain settings are being used. The deprecated `.getDrawable()` must be used until the minimum API >= 21. - if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) { - urlRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_dark_blue)); + // Set a background on the URL relative layout to indicate that custom domain settings are being used. + if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { + urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.drawable.url_bar_background_light_green, null)); } else { - urlRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_light_green)); + urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.drawable.url_bar_background_dark_blue, null)); } } else { // The new URL does not have custom domain settings. Load the defaults. // Store the values from the shared preferences. - boolean defaultJavaScriptEnabled = sharedPreferences.getBoolean("javascript", false); + nestedScrollWebView.getSettings().setJavaScriptEnabled(sharedPreferences.getBoolean("javascript", false)); nestedScrollWebView.setAcceptFirstPartyCookies(sharedPreferences.getBoolean("first_party_cookies", false)); boolean defaultThirdPartyCookiesEnabled = sharedPreferences.getBoolean("third_party_cookies", false); nestedScrollWebView.getSettings().setDomStorageEnabled(sharedPreferences.getBoolean("dom_storage", false)); @@ -4128,16 +4219,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook nestedScrollWebView.enableBlocklist(NestedScrollWebView.ULTRALIST, sharedPreferences.getBoolean("ultralist", true)); nestedScrollWebView.enableBlocklist(NestedScrollWebView.ULTRAPRIVACY, 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 (nestedScrollWebView.getNightMode()) { - // Enable JavaScript. - nestedScrollWebView.getSettings().setJavaScriptEnabled(true); - } else { - // Set JavaScript according to the domain settings. - nestedScrollWebView.getSettings().setJavaScriptEnabled(defaultJavaScriptEnabled); - } + String webViewTheme = sharedPreferences.getString("webview_theme", getString(R.string.webview_theme_default_value)); // Apply the default first-party cookie setting. cookieManager.setAcceptCookie(nestedScrollWebView.getAcceptFirstPartyCookies()); @@ -4201,14 +4283,41 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook nestedScrollWebView.getSettings().setUserAgentString(userAgentDataArray[userAgentArrayPosition]); } + // Get the WebView theme entry values string array. + String[] webViewThemeEntryValuesStringArray = getResources().getStringArray(R.array.webview_theme_entry_values); + + // Apply the WebView theme if supported by the installed WebView. + if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) { + // Set the WebView theme. A switch statement cannot be used because the WebView theme entry values string array is not a compile time constant. + if (webViewTheme.equals(webViewThemeEntryValuesStringArray[1])) { // The light theme is selected. + // Turn off the WebView dark mode. + WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF); + } else if (webViewTheme.equals(webViewThemeEntryValuesStringArray[2])) { // The dark theme is selected. + // Turn on the WebView dark mode. + WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON); + } else { // The system default theme is selected. + // Get the current system theme status. + int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; + + // Set the WebView theme according to the current system theme status. + if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { // The system is in day mode. + // Turn off the WebView dark mode. + WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF); + } else { // The system is in night mode. + // Turn on the WebView dark mode. + WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON); + } + } + } + // Set the viewport. nestedScrollWebView.getSettings().setUseWideViewPort(wideViewport); // Set the loading of webpage images. nestedScrollWebView.getSettings().setLoadsImagesAutomatically(displayWebpageImages); - // Set a transparent background on URL edit text. The deprecated `getResources().getDrawable()` must be used until the minimum API >= 21. - urlRelativeLayout.setBackground(getResources().getDrawable(R.color.transparent)); + // Set a transparent background on URL edit text. + urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.color.transparent, null)); } // Close the domains database helper. @@ -4225,9 +4334,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } private void applyProxy(boolean reloadWebViews) { - // Get a handle for the app bar layout. - AppBarLayout appBarLayout = findViewById(R.id.appbar_layout); - // Set the proxy according to the mode. `this` refers to the current activity where an alert dialog might be displayed. ProxyHelper.setProxy(getApplicationContext(), appBarLayout, proxyMode); @@ -4255,10 +4361,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook case ProxyHelper.TOR: // Set the app bar background to indicate proxying through Orbot is enabled. - if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) { - appBarLayout.setBackgroundResource(R.color.dark_blue_30); - } else { + if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { appBarLayout.setBackgroundResource(R.color.blue_50); + } else { + appBarLayout.setBackgroundResource(R.color.dark_blue_30); } // Check to see if Orbot is installed. @@ -4297,10 +4403,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook case ProxyHelper.I2P: // Set the app bar background to indicate proxying through Orbot is enabled. - if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) { - appBarLayout.setBackgroundResource(R.color.dark_blue_30); - } else { + if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { appBarLayout.setBackgroundResource(R.color.blue_50); + } else { + appBarLayout.setBackgroundResource(R.color.dark_blue_30); } // Check to see if I2P is installed. @@ -4324,10 +4430,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook case ProxyHelper.CUSTOM: // Set the app bar background to indicate proxying through Orbot is enabled. - if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) { - appBarLayout.setBackgroundResource(R.color.dark_blue_30); - } else { + if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { appBarLayout.setBackgroundResource(R.color.blue_50); + } else { + appBarLayout.setBackgroundResource(R.color.dark_blue_30); } break; } @@ -4379,10 +4485,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook if (currentWebView.getAcceptFirstPartyCookies()) { // First-party cookies are enabled. firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_enabled); } else { // First-party cookies are disabled. - if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) { - firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_disabled_night); - } else { + if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_disabled_day); + } else { + firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_disabled_night); } } @@ -4390,24 +4496,24 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook if (currentWebView.getSettings().getJavaScriptEnabled() && currentWebView.getSettings().getDomStorageEnabled()) { // Both JavaScript and DOM storage are enabled. domStorageMenuItem.setIcon(R.drawable.dom_storage_enabled); } else if (currentWebView.getSettings().getJavaScriptEnabled()) { // JavaScript is enabled but DOM storage is disabled. - if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) { - domStorageMenuItem.setIcon(R.drawable.dom_storage_disabled_night); - } else { + if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { domStorageMenuItem.setIcon(R.drawable.dom_storage_disabled_day); + } else { + domStorageMenuItem.setIcon(R.drawable.dom_storage_disabled_night); } } else { // JavaScript is disabled, so DOM storage is ghosted. - if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) { - domStorageMenuItem.setIcon(R.drawable.dom_storage_ghosted_night); - } else { + if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { domStorageMenuItem.setIcon(R.drawable.dom_storage_ghosted_day); + } else { + domStorageMenuItem.setIcon(R.drawable.dom_storage_ghosted_night); } } // Update the refresh icon. - if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) { - refreshMenuItem.setIcon(R.drawable.refresh_enabled_night); - } else { + if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { refreshMenuItem.setIcon(R.drawable.refresh_enabled_day); + } else { + refreshMenuItem.setIcon(R.drawable.refresh_enabled_night); } // `invalidateOptionsMenu()` calls `onPrepareOptionsMenu()` and redraws the icons in the app bar. @@ -4633,8 +4739,92 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook ultraList = combinedBlocklists.get(4); ultraPrivacy = combinedBlocklists.get(5); - // Add the first tab. - addNewTab("", true); + // Check to see if the activity has been restarted. + if ((savedStateArrayList == null) || (savedStateArrayList.size() == 0)) { // The activity has not been restarted or it was restarted on start to force the night theme. + // Add the first tab. + addNewTab("", true); + } else { // The activity has been restarted. + // Restore each tab. Once the minimum API >= 24, a `forEach()` command can be used. + for (int i = 0; i < savedStateArrayList.size(); i++) { + // Add a new tab. + tabLayout.addTab(tabLayout.newTab()); + + // Get the new tab. + TabLayout.Tab newTab = tabLayout.getTabAt(i); + + // Remove the lint warning below that the current tab might be null. + assert newTab != null; + + // Set a custom view on the new tab. + newTab.setCustomView(R.layout.tab_custom_view); + + // Add the new page. + webViewPagerAdapter.restorePage(savedStateArrayList.get(i), savedNestedScrollWebViewStateArrayList.get(i)); + } + + // Reset the saved state variables. + savedStateArrayList = null; + savedNestedScrollWebViewStateArrayList = null; + + // Restore the selected tab position. + if (savedTabPosition == 0) { // The first tab is selected. + // Set the first page as the current WebView. + setCurrentWebView(0); + } else { // the first tab is not selected. + // Move to the selected tab. + webViewPager.setCurrentItem(savedTabPosition); + } + + // Get the intent that started the app. + Intent intent = getIntent(); + + // Get the information from the intent. + String intentAction = intent.getAction(); + Uri intentUriData = intent.getData(); + + // Determine if this is a web search. + boolean isWebSearch = ((intentAction != null) && intentAction.equals(Intent.ACTION_WEB_SEARCH)); + + // 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 || isWebSearch) { + // Get the shared preferences. + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); + + // Create a URL string. + String url; + + // If the intent action is a web search, perform the search. + if (isWebSearch) { // The intent is a web search. + // Create an encoded URL string. + String encodedUrlString; + + // Sanitize the search input and convert it to a search. + try { + encodedUrlString = URLEncoder.encode(intent.getStringExtra(SearchManager.QUERY), "UTF-8"); + } catch (UnsupportedEncodingException exception) { + encodedUrlString = ""; + } + + // Add the base search URL. + url = searchURL + encodedUrlString; + } else { // The intent should contain a URL. + // Set the intent data as the url. + url = intentUriData.toString(); + } + + // Add a new tab if specified in the preferences. + if (sharedPreferences.getBoolean("open_intents_in_new_tab", true)) { // Load the URL in a new tab. + // Set the loading new intent flag. + loadingNewIntent = true; + + // Add a new tab. + addNewTab(url, true); + } else { // Load the URL in the current tab. + // Make it so. + loadUrl(currentWebView, url); + } + } + } } public void addTab(View view) { @@ -4643,13 +4833,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } private void addNewTab(String url, boolean moveToTab) { - // Sanitize the URL. - url = sanitizeUrl(url); - - // Get a handle for the tab layout and the view pager. - TabLayout tabLayout = findViewById(R.id.tablayout); - ViewPager webViewPager = findViewById(R.id.webviewpager); - // Get the new page number. The page numbers are 0 indexed, so the new page number will match the current count. int newTabNumber = tabLayout.getTabCount(); @@ -4670,9 +4853,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } public void closeTab(View view) { - // Get a handle for the tab layout. - TabLayout tabLayout = findViewById(R.id.tablayout); - // Run the command according to the number of tabs. if (tabLayout.getTabCount() > 1) { // There is more than one tab open. // Close the current tab. @@ -4683,11 +4863,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } private void closeCurrentTab() { - // Get handles for the views. - AppBarLayout appBarLayout = findViewById(R.id.appbar_layout); - TabLayout tabLayout = findViewById(R.id.tablayout); - ViewPager webViewPager = findViewById(R.id.webviewpager); - // Get the current tab number. int currentTabNumber = tabLayout.getSelectedTabPosition(); @@ -4961,14 +5136,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get the current theme status. int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; - // Set a green background on the URL relative layout to indicate that custom domain settings are being used. The deprecated `.getDrawable()` must be used until the minimum API >= 21. - if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) { - urlRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_dark_blue)); + // Set a green background on the URL relative layout to indicate that custom domain settings are being used. + if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { + urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.drawable.url_bar_background_light_green, null)); } else { - urlRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_light_green)); + urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.drawable.url_bar_background_dark_blue, null)); } } else { - urlRelativeLayout.setBackground(getResources().getDrawable(R.color.transparent)); + urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.color.transparent, null)); } } else { // The fragment has not been populated. Try again in 100 milliseconds. // Create a handler to set the current WebView. @@ -4986,7 +5161,48 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } @Override - public void initializeWebView(NestedScrollWebView nestedScrollWebView, int pageNumber, ProgressBar progressBar, String url) { + public void initializeWebView(NestedScrollWebView nestedScrollWebView, int pageNumber, ProgressBar progressBar, String url, Boolean restoringState) { + // Get a handle for the shared preferences. + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); + + // Get the WebView theme. + String webViewTheme = sharedPreferences.getString("webview_theme", getString(R.string.webview_theme_default_value)); + + // Get the WebView theme entry values string array. + String[] webViewThemeEntryValuesStringArray = getResources().getStringArray(R.array.webview_theme_entry_values); + + // Apply the WebView theme if supported by the installed WebView. + if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) { + // Set the WebView theme. A switch statement cannot be used because the WebView theme entry values string array is not a compile time constant. + if (webViewTheme.equals(webViewThemeEntryValuesStringArray[1])) { // The light theme is selected. + // Turn off the WebView dark mode. + WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF); + + // Make the WebView visible. The WebView was created invisible in `webview_framelayout` to prevent a white background splash in night mode. + // If the system is currently in night mode, showing the WebView will be handled in `onProgressChanged()`. + nestedScrollWebView.setVisibility(View.VISIBLE); + } else if (webViewTheme.equals(webViewThemeEntryValuesStringArray[2])) { // The dark theme is selected. + // Turn on the WebView dark mode. + WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON); + } else { // The system default theme is selected. + // Get the current system theme status. + int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; + + // Set the WebView theme according to the current system theme status. + if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { // The system is in day mode. + // Turn off the WebView dark mode. + WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF); + + // Make the WebView visible. The WebView was created invisible in `webview_framelayout` to prevent a white background splash in night mode. + // If the system is currently in night mode, showing the WebView will be handled in `onProgressChanged()`. + nestedScrollWebView.setVisibility(View.VISIBLE); + } else { // The system is in night mode. + // Turn on the WebView dark mode. + WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON); + } + } + } + // Get a handle for the app compat delegate. AppCompatDelegate appCompatDelegate = getDelegate(); @@ -4997,7 +5213,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook ActionBar actionBar = appCompatDelegate.getSupportActionBar(); LinearLayout tabsLinearLayout = findViewById(R.id.tabs_linearlayout); EditText urlEditText = findViewById(R.id.url_edittext); - TabLayout tabLayout = findViewById(R.id.tablayout); SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout); // Remove the incorrect lint warning below that the action bar might be null. @@ -5015,9 +5230,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Remove the lint warning below that the input method manager might be null. assert inputMethodManager != null; - // Get a handle for the shared preferences. - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); - // Initialize the favorite icon. nestedScrollWebView.initializeFavoriteIcon(); @@ -5234,46 +5446,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook }); } - // TODO. - if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) { - WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_AUTO); - } - // 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 (nestedScrollWebView.getNightMode()) { // Night mode is enabled. - // `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. - // `::selection {background: #0D47A1}' sets the text selection highlight color to be a dark blue. `!important` takes precedent over any existing sub-settings. - nestedScrollWebView.evaluateJavascript("(function() {var parent = document.getElementsByTagName('head').item(0); var style = document.createElement('style'); style.type = 'text/css'; " + - "style.innerHTML = '* {background-color: #212121 !important; color: #BDBDBD !important; box-shadow: none !important; text-decoration: none !important;" + - "text-shadow: none !important; border: none !important;} a {color: #1565C0 !important;} ::selection {background: #0D47A1 !important;}'; parent.appendChild(style)})()", value -> { - // Initialize a handler to display `mainWebView`. - Handler displayWebViewHandler = new Handler(); - - // Setup a runnable to display `mainWebView` after a delay to allow the CSS to be applied. - Runnable displayWebViewRunnable = () -> { - // Only display `mainWebView` if the progress bar is gone. This prevents the display of the `WebView` while it is still loading. - if (progressBar.getVisibility() == View.GONE) { - nestedScrollWebView.setVisibility(View.VISIBLE); - } - }; - - // Display the WebView after 500 milliseconds. - displayWebViewHandler.postDelayed(displayWebViewRunnable, 500); - }); - } else { // Night mode is disabled. - // 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. - nestedScrollWebView.setVisibility(View.VISIBLE); - } - // Update the progress bar. progressBar.setProgress(progress); @@ -5287,6 +5464,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook //Stop the swipe to refresh indicator if it is running swipeRefreshLayout.setRefreshing(false); + + // Make the current WebView visible. If this is a new tab, the current WebView would have been created invisible in `webview_framelayout` to prevent a white background splash in night mode. + nestedScrollWebView.setVisibility(View.VISIBLE); } } @@ -5335,19 +5515,19 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get the custom view from the tab. View tabView = tab.getCustomView(); - // Remove the incorrect warning below that the current tab view might be null. - assert tabView != null; - - // Get the title text view from the tab. - TextView tabTitleTextView = tabView.findViewById(R.id.title_textview); + // Only populate the title text view if the tab view has been fully populated. + if (tabView != null) { + // Get the title text view from the tab. + TextView tabTitleTextView = tabView.findViewById(R.id.title_textview); - // Set the title according to the URL. - if (title.equals("about:blank")) { - // Set the title to indicate a new tab. - tabTitleTextView.setText(R.string.new_tab); - } else { - // Set the title as the tab text. - tabTitleTextView.setText(title); + // Set the title according to the URL. + if (title.equals("about:blank")) { + // Set the title to indicate a new tab. + tabTitleTextView.setText(R.string.new_tab); + } else { + // Set the title as the tab text. + tabTitleTextView.setText(title); + } } } } @@ -5504,7 +5684,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Apply the domain settings for the new URL. This doesn't do anything if the domain has not changed. applyDomainSettings(nestedScrollWebView, url, true, false); - // Manually load the URL. The changing of the user agent will cause WebView to reload the previous URL. + // Load the URL. By using `loadUrl()`, instead of `loadUrlFromBase()`, the Referer header will never be sent. nestedScrollWebView.loadUrl(url, customHeaders); // Returning true indicates that Privacy Browser is manually handling the loading of the URL. @@ -5596,8 +5776,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get a handle for the navigation menu. Menu navigationMenu = navigationView.getMenu(); - // Get a handle for the navigation requests menu item. The menu is 0 based. - MenuItem navigationRequestsMenuItem = navigationMenu.getItem(6); + // Get a handle for the navigation requests menu item. + MenuItem navigationRequestsMenuItem = navigationMenu.findItem(R.id.requests); // Create an empty web resource response to be used if the resource request is blocked. WebResourceResponse emptyWebResourceResponse = new WebResourceResponse("text/plain", "utf8", new ByteArrayInputStream("".getBytes())); @@ -5939,9 +6119,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get the preferences. boolean scrollAppBar = sharedPreferences.getBoolean("scroll_app_bar", true); - // Get a handler for the app bar layout. - AppBarLayout appBarLayout = findViewById(R.id.appbar_layout); - // 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. @@ -5966,14 +6143,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Reset the requests counters. nestedScrollWebView.resetRequestsCounters(); - // TODO. Make the background of a new tab match the theme. - // If night mode is enabled, hide `mainWebView` until after the night mode CSS is applied. - if (nestedScrollWebView.getNightMode()) { - nestedScrollWebView.setVisibility(View.INVISIBLE); - } else { - nestedScrollWebView.setVisibility(View.VISIBLE); - } - // Hide the keyboard. inputMethodManager.hideSoftInputFromWindow(nestedScrollWebView.getWindowToken(), 0); @@ -6018,10 +6187,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; // Set the stop icon according to the theme. - if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) { - refreshMenuItem.setIcon(R.drawable.close_night); - } else { + if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { refreshMenuItem.setIcon(R.drawable.close_day); + } else { + refreshMenuItem.setIcon(R.drawable.close_night); } } } @@ -6051,10 +6220,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; // Set the icon according to the theme. - if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) { - refreshMenuItem.setIcon(R.drawable.refresh_enabled_night); - } else { + if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { refreshMenuItem.setIcon(R.drawable.refresh_enabled_day); + } else { + refreshMenuItem.setIcon(R.drawable.refresh_enabled_night); } } } @@ -6209,14 +6378,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } }); - // Check to see if this is the first page. - if (pageNumber == 0) { + // Check to see if the state is being restored. + if (restoringState) { // The state is being restored. + // Resume the nested scroll WebView JavaScript timers. + nestedScrollWebView.resumeTimers(); + } else if (pageNumber == 0) { // The first page is being loaded. // Set this nested scroll WebView as the current WebView. currentWebView = nestedScrollWebView; - // Apply the app settings from the shared preferences. - applyAppSettings(); - // Initialize the URL to load string. String urlToLoadString; @@ -6244,6 +6413,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } else if (launchingIntentUriData != null){ // The intent contains a URL. // Store the URL. urlToLoadString = launchingIntentUriData.toString(); + } else if (!url.equals("")) { // The activity has been restarted. + // Load the saved URL. + urlToLoadString = url; } else { // The is no URL in the intent. // Store the homepage to be loaded. urlToLoadString = sharedPreferences.getString("homepage", getString(R.string.homepage_default_value)); @@ -6256,11 +6428,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook loadUrl(nestedScrollWebView, urlToLoadString); } } else { // This is not the first tab. - // Apply the domain settings. - applyDomainSettings(nestedScrollWebView, url, false, false); - // Load the URL. - nestedScrollWebView.loadUrl(url, customHeaders); + loadUrl(nestedScrollWebView, url); // Set the focus and display the keyboard if the URL is blank. if (url.equals("")) {