X-Git-Url: https://gitweb.stoutner.com/?a=blobdiff_plain;f=app%2Fsrc%2Fmain%2Fjava%2Fcom%2Fstoutner%2Fprivacybrowser%2Factivities%2FMainWebViewActivity.java;h=0d8534cbf7a51fffda56048163219219f0160f31;hb=cdda3a0ae5545d3d3fd5033444c7bda976bdd581;hp=a0046280162d4c7bb62edad317322c5cb17f6b7d;hpb=0ef5e9e86c340e134f78f072b2449bb29179ec28;p=PrivacyBrowserAndroid.git diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java index a0046280..0d8534cb 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java @@ -59,7 +59,9 @@ import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatDialogFragment; import android.support.v7.widget.Toolbar; import android.text.Editable; +import android.text.Spanned; import android.text.TextWatcher; +import android.text.style.ForegroundColorSpan; import android.util.Patterns; import android.view.ContextMenu; import android.view.GestureDetector; @@ -102,6 +104,7 @@ import com.stoutner.privacybrowser.dialogs.SslCertificateErrorDialog; import java.io.BufferedReader; import java.io.ByteArrayInputStream; +import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; @@ -118,8 +121,10 @@ import java.util.Set; public class MainWebViewActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener, CreateHomeScreenShortcutDialog.CreateHomeScreenSchortcutListener, SslCertificateErrorDialog.SslCertificateErrorListener, DownloadFileDialog.DownloadFileListener, DownloadImageDialog.DownloadImageListener, UrlHistoryDialog.UrlHistoryListener { - // `appBar` is public static so it can be accessed from `OrbotProxyHelper`. It is also used in `onCreate()`, `onOptionsItemSelected()`, `closeFindOnPage()`, and `applyAppSettings()`. - public static ActionBar appBar; + // `darkTheme` is public static so it can be accessed from `AboutActivity`, `GuideActivity`, `AddDomainDialog`, `SettingsActivity`, `DomainsActivity`, `DomainsListFragment`, `BookmarksActivity`, `BookmarksDatabaseViewActivity`, + // `CreateBookmarkDialog`, `CreateBookmarkFolderDialog`, `DownloadFileDialog`, `DownloadImageDialog`, `EditBookmarkDialog`, `EditBookmarkFolderDialog`, `MoveToFolderDialog`, `SslCertificateErrorDialog`, `UrlHistoryDialog`, `ViewSslCertificateDialog`, + // `CreateHomeScreenShortcutDialog`, and `OrbotProxyHelper`. It is also used in `onCreate()`, `applyAppSettings()`, `applyDomainSettings()`, and `updatePrivacyIcons()`. + public static boolean darkTheme; // `favoriteIconBitmap` is public static so it can be accessed from `CreateHomeScreenShortcutDialog`, `BookmarksActivity`, `CreateBookmarkDialog`, `CreateBookmarkFolderDialog`, `EditBookmarkDialog`, `EditBookmarkFolderDialog`, `ViewSslCertificateDialog`. // It is also used in `onCreate()`, `onCreateHomeScreenShortcutCreate()`, and `applyDomainSettings`. @@ -145,6 +150,9 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation public static boolean reloadOnRestartBoolean; + // `appBar` is used in `onCreate()`, `onOptionsItemSelected()`, `closeFindOnPage()`, and `applyAppSettings()`. + private ActionBar appBar; + // `navigatingHistory` is used in `onCreate()`, `onNavigationItemSelected()`, and `applyDomainSettings()`. private boolean navigatingHistory; @@ -207,7 +215,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation // `adBlockerEnabled` is used in `onCreate()` and `applyAppSettings()`. private boolean adBlockerEnabled; - // `privacyBrowserRuntime` is used in `onCreate()` and `applyAppSettings()`. + // `privacyBrowserRuntime` is used in `onCreate()`, `onOptionsItemSelected()`, and `applyAppSettings()`. private Runtime privacyBrowserRuntime; // `incognitoModeEnabled` is used in `onCreate()` and `applyAppSettings()`. @@ -243,7 +251,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation // `waitingForOrbotData` is used in `onCreate()` and `applyAppSettings()`. private String waitingForOrbotHTMLString; - // `privateDataDirectoryString` is used in `onCreate()` and `onNavigationItemSelected()`. + // `privateDataDirectoryString` is used in `onCreate()`, `onOptionsItemSelected()`, and `onNavigationItemSelected()`. private String privateDataDirectoryString; // `findOnPageLinearLayout` is used in `onCreate()`, `onOptionsItemSelected()`, and `closeFindOnPage()`. @@ -261,9 +269,18 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation // `supportAppBar` is used in `onCreate()`, `onOptionsItemSelected()`, and `closeFindOnPage()`. private Toolbar supportAppBar; - // `urlTextBox` is used in `onCreate()`, `onOptionsItemSelected()`, `loadUrlFromTextBox()`, and `loadUrl()`. + // `urlTextBox` is used in `onCreate()`, `onOptionsItemSelected()`, `loadUrlFromTextBox()`, `loadUrl()`, and `highlightUrlText()`. private EditText urlTextBox; + // `redColorSpan` is used in `onCreate()` and `highlightUrlText()`. + private ForegroundColorSpan redColorSpan; + + // `initialGrayColorSpan` is sued in `onCreate()` and `highlightUrlText()`. + private ForegroundColorSpan initialGrayColorSpan; + + // `finalGrayColorSpam` is used in `onCreate()` and `highlightUrlText()`. + private ForegroundColorSpan finalGrayColorSpan; + // `adView` is used in `onCreate()` and `onConfigurationChanged()`. private View adView; @@ -276,11 +293,32 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation // `mainWebViewRelativeLayout` is used in `onCreate()` and `onNavigationItemSelected()`. private RelativeLayout mainWebViewRelativeLayout; + // `urlIsLoading` is used in `onCreate()`, `loadUrl()`, and `applyDomainSettings()`. + private boolean urlIsLoading; + @Override // Remove Android Studio's warning about the dangers of using SetJavaScriptEnabled. The whole premise of Privacy Browser is built around an understanding of these dangers. @SuppressLint("SetJavaScriptEnabled") + // Remove Android Studio's warning about deprecations. We have to use the deprecated `getColor()` until API >= 23. + @SuppressWarnings("deprecation") protected void onCreate(Bundle savedInstanceState) { + // Get a handle for `sharedPreferences`. `this` references the current context. + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); + + // Get the theme preference. + darkTheme = sharedPreferences.getBoolean("dark_theme", false); + + // Set the activity theme. + if (darkTheme) { + setTheme(R.style.PrivacyBrowserDark); + } else { + setTheme(R.style.PrivacyBrowserLight); + } + + // Run the default commands. super.onCreate(savedInstanceState); + + // Set the content view. setContentView(R.layout.main_drawerlayout); // Get a handle for `inputMethodManager`. @@ -298,8 +336,31 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation appBar.setCustomView(R.layout.url_app_bar); appBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM); - // Set the "go" button on the keyboard to load the URL in urlTextBox. + // Initialize the `ForegroundColorSpans` and `StyleSpan` for highlighting `urlTextBox`. We have to use the deprecated `getColor()` until API >= 23. + redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700)); + initialGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500)); + finalGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500)); + + // Get a handle for `urlTextBox`. urlTextBox = (EditText) appBar.getCustomView().findViewById(R.id.url_edittext); + + // Remove the formatting from `urlTextBar` when the user is editing the text. + urlTextBox.setOnFocusChangeListener(new View.OnFocusChangeListener() { + @Override + public void onFocusChange(View v, boolean hasFocus) { + if (hasFocus) { // The user is editing `urlTextBox`. + // Remove the highlighting. + urlTextBox.getText().removeSpan(redColorSpan); + urlTextBox.getText().removeSpan(initialGrayColorSpan); + urlTextBox.getText().removeSpan(finalGrayColorSpan); + } else { // The user has stopped editing `urlTextBox`. + // Reapply the highlighting. + highlightUrlText(); + } + } + }); + + // Set the `Go` button on the keyboard to load the URL in `urlTextBox`. urlTextBox.setOnKeyListener(new View.OnKeyListener() { @Override public boolean onKey(View v, int keyCode, KeyEvent event) { @@ -547,8 +608,11 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation navigationForwardMenuItem.setEnabled(mainWebView.canGoForward()); navigationHistoryMenuItem.setEnabled((mainWebView.canGoBack() || mainWebView.canGoForward())); - // Hide the keyboard so we can see the navigation menu. `0` indicates no additional flags. + // Hide the keyboard (if displayed) so we can see the navigation menu. `0` indicates no additional flags. inputMethodManager.hideSoftInputFromWindow(mainWebView.getWindowToken(), 0); + + // Clear the focus from `urlTextBox` if it has it. + urlTextBox.clearFocus(); } } }); @@ -648,27 +712,33 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation // Update the URL in urlTextBox when the page starts to load. @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { - // Reset `webViewTitle` - webViewTitle = getString(R.string.no_title); - // Check to see if we are waiting on Orbot. if (!waitingForOrbot) { // We are not waiting on Orbot, so we need to process the URL. // We need to update `formattedUrlString` at the beginning of the load, so that if the user toggles JavaScript during the load the new website is reloaded. formattedUrlString = url; - // Display the loading URL is the URL text box. - urlTextBox.setText(url); + // Display the formatted URL text. + urlTextBox.setText(formattedUrlString); + + // Apply text highlighting to `urlTextBox`. + highlightUrlText(); // Apply any custom domain settings if the URL was loaded by navigating history. if (navigatingHistory) { applyDomainSettings(url); } + + // Set `urlIsLoading` to `true`, so that redirects while loading do not trigger changes in the user agent, which forces another reload of the existing page. + urlIsLoading = true; } } // Update formattedUrlString and urlTextBox. It is necessary to do this after the page finishes loading because the final URL can change during load. @Override public void onPageFinished(WebView view, String url) { + // Reset `urlIsLoading`, which is used to prevent reloads on redirect if the user agent changes. + urlIsLoading = false; + // Clear the cache and history if Incognito Mode is enabled. if (incognitoModeEnabled) { // Clear the cache. `true` includes disk files. @@ -696,7 +766,6 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation // Set `formattedUrlString` to `""`. formattedUrlString = ""; - // Update `urlTextBox`. urlTextBox.setText(formattedUrlString); // Request focus for `urlTextBox`. @@ -705,7 +774,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation // Display the keyboard. inputMethodManager.showSoftInput(urlTextBox, 0); - // Apply the domain settings. + // Apply the domain settings. This clears any settings from the previous domain. applyDomainSettings(formattedUrlString); } else { // `WebView` has loaded a webpage. // Set `formattedUrlString`. @@ -713,7 +782,11 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation // Only update `urlTextBox` if the user is not typing in it. if (!urlTextBox.hasFocus()) { + // Display the formatted URL text. urlTextBox.setText(formattedUrlString); + + // Apply text highlighting to `urlTextBox`. + highlightUrlText(); } } @@ -1033,6 +1106,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation MenuItem toggleDomStorageMenuItem = menu.findItem(R.id.toggle_dom_storage); MenuItem toggleSaveFormDataMenuItem = menu.findItem(R.id.toggle_save_form_data); MenuItem clearCookiesMenuItem = menu.findItem(R.id.clear_cookies); + MenuItem clearDOMStorageMenuItem = menu.findItem(R.id.clear_dom_storage); MenuItem clearFormDataMenuItem = menu.findItem(R.id.clear_form_data); MenuItem fontSizeMenuItem = menu.findItem(R.id.font_size); MenuItem displayImagesMenuItem = menu.findItem(R.id.display_images); @@ -1048,13 +1122,30 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation // Enable third-party cookies if first-party cookies are enabled. toggleThirdPartyCookiesMenuItem.setEnabled(firstPartyCookiesEnabled); - // Enable DOM Storage if JavaScript is enabled. + // Enable `DOM Storage` if JavaScript is enabled. toggleDomStorageMenuItem.setEnabled(javaScriptEnabled); - // Enable Clear Cookies if there are any. + // Enable `Clear Cookies` if there are any. clearCookiesMenuItem.setEnabled(cookieManager.hasCookies()); - // Enable Clear Form Data is there is any. + // Get a count of the number of files in the `Local Storage` directory. + File localStorageDirectory = new File (privateDataDirectoryString + "/app_webview/Local Storage/"); + int localStorageDirectoryNumberOfFiles = 0; + if (localStorageDirectory.exists()) { + localStorageDirectoryNumberOfFiles = localStorageDirectory.list().length; + } + + // Get a count of the number of files in the `IndexedDB` directory. + File indexedDBDirectory = new File (privateDataDirectoryString + "/app_webview/IndexedDB"); + int indexedDBDirectoryNumberOfFiles = 0; + if (indexedDBDirectory.exists()) { + indexedDBDirectoryNumberOfFiles = indexedDBDirectory.list().length; + } + + // Enable `Clear DOM Storage` if there is any. + clearDOMStorageMenuItem.setEnabled(localStorageDirectoryNumberOfFiles > 0 || indexedDBDirectoryNumberOfFiles > 0); + + // Enable `Clear Form Data` is there is any. WebViewDatabase mainWebViewDatabase = WebViewDatabase.getInstance(this); clearFormDataMenuItem.setEnabled(mainWebViewDatabase.hasFormData()); @@ -1309,6 +1400,13 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation // Delete the DOM Storage. WebStorage webStorage = WebStorage.getInstance(); webStorage.deleteAllData(); + + // Manually remove `IndexedDB` if it exists. + try { + privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/IndexedDB"); + } catch (IOException e) { + // Do nothing if an error is thrown. + } } } }) @@ -1389,12 +1487,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation case R.id.share: // Setup the share string. - String shareString; - if (webViewTitle != null) { - shareString = webViewTitle + " – " + urlTextBox.getText().toString(); - } else { - shareString = urlTextBox.getText().toString(); - } + String shareString = webViewTitle + " – " + urlTextBox.getText().toString(); // Create the share intent. Intent shareIntent = new Intent(); @@ -1413,8 +1506,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation // Show the Find on Page `RelativeLayout`. findOnPageLinearLayout.setVisibility(View.VISIBLE); - // Display the keyboard. We have to wait 200 ms before running the command to work around a bug in Android. - // http://stackoverflow.com/questions/5520085/android-show-softkeyboard-with-showsoftinput-is-not-working + // Display the keyboard. We have to wait 200 ms before running the command to work around a bug in Android. http://stackoverflow.com/questions/5520085/android-show-softkeyboard-with-showsoftinput-is-not-working findOnPageEditText.postDelayed(new Runnable() { @Override @@ -1423,7 +1515,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation // Set the focus on `findOnPageEditText`. findOnPageEditText.requestFocus(); - // Display the keyboard. + // Display the keyboard. `0` sets no input flags. inputMethodManager.showSoftInput(findOnPageEditText, 0); } }, 200); @@ -1575,10 +1667,16 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation WebStorage webStorage = WebStorage.getInstance(); webStorage.deleteAllData(); - // Manually delete the DOM storage directory, as `WebStorage` sometimes will not flush its changes to disk before `System.exit(0)` is run. + // Manually delete the DOM storage files and directories, as `WebStorage` sometimes will not flush its changes to disk before `System.exit(0)` is run. try { // We have to use a `String[]` because the directory contains a space and `Runtime.exec` will not escape the string correctly otherwise. privacyBrowserRuntime.exec(new String[] {"rm", "-rf", privateDataDirectoryString + "/app_webview/Local Storage/"}); + + // We have to use multiple commands because `Runtime.exec()` does not like `*`. + privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/IndexedDB"); + privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager"); + privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager-journal"); + privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/databases"); } catch (IOException e) { // Do nothing if an error is thrown. } @@ -2090,6 +2188,9 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation // Load the URL. mainWebView.loadUrl(url, customHeaders); + + // Set `urlIsLoading` to prevent changes in the user agent on websites with redirects from reloading the current website. + urlIsLoading = true; } public void findPreviousOnPage(View view) { @@ -2160,6 +2261,13 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation // 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. + if (darkTheme) { + appBar.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.dark_blue_30)); + } else { + appBar.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.blue_50)); + } + // Display a message to the user if we are waiting on Orbot. if (!orbotStatus.equals("ON")) { // Set `waitingForOrbot`. @@ -2187,6 +2295,13 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation // 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. + if (darkTheme) { + appBar.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.gray_900)); + } else { + appBar.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.gray_100)); + } + // Reset `waitingForOrbot. waitingForOrbot = false; } @@ -2346,6 +2461,11 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation // Get a handle for the shared preference. `this` references the current context. SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); + // Store the default font size and user agent information. + String defaultFontSizeString = sharedPreferences.getString("default_font_size", "100"); + String defaultUserAgentString = sharedPreferences.getString("user_agent", "PrivacyBrowser/1.0"); + String defaultCustomUserAgentString = sharedPreferences.getString("custom_user_agent", "PrivacyBrowser/1.0"); + if (domainSettingsApplied) { // The url we are loading has custom domain settings. // Get a cursor for the current host and move it to the first position. Cursor currentHostDomainSettingsCursor = domainsDatabaseHelper.getCursorForDomainName(domainNameInDatabase); @@ -2369,24 +2489,58 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation cookieManager.setAcceptCookie(firstPartyCookiesEnabled); mainWebView.getSettings().setDomStorageEnabled(domStorageEnabled); mainWebView.getSettings().setSaveFormData(saveFormDataEnabled); - mainWebView.getSettings().setTextZoom(fontSize); + + // Apply the font size. + if (fontSize == 0) { // Apply the default font size. + mainWebView.getSettings().setTextZoom(Integer.valueOf(defaultFontSizeString)); + } else { // Apply the specified font size. + mainWebView.getSettings().setTextZoom(fontSize); + } // Set third-party cookies status if API >= 21. if (Build.VERSION.SDK_INT >= 21) { cookieManager.setAcceptThirdPartyCookies(mainWebView, thirdPartyCookiesEnabled); } - // Set the user agent. - if (userAgentString.equals("WebView default user agent")) { - // Set the user agent to `""`, which uses the default value. - mainWebView.getSettings().setUserAgentString(""); - } else { - // Use the selected user agent. - mainWebView.getSettings().setUserAgentString(userAgentString); + // Only set the user agent if the webpage is not currently loading. Otherwise, changing the user agent on redirects can cause the original website to reload. + if (!urlIsLoading) { + switch (userAgentString) { + case "System default user agent": + // Set the user agent according to the system default. + switch (defaultUserAgentString) { + case "WebView default user agent": + // Set the user agent to `""`, which uses the default value. + mainWebView.getSettings().setUserAgentString(""); + break; + + case "Custom user agent": + // Set the custom user agent. + mainWebView.getSettings().setUserAgentString(defaultCustomUserAgentString); + break; + + default: + // Use the selected user agent. + mainWebView.getSettings().setUserAgentString(defaultUserAgentString); + } + break; + + case "WebView default user agent": + // Set the user agent to `""`, which uses the default value. + mainWebView.getSettings().setUserAgentString(""); + break; + + default: + // Use the selected user agent. + mainWebView.getSettings().setUserAgentString(userAgentString); + } } // Set a green background on `urlTextBox` to indicate that custom domain settings are being used. We have to use the deprecated `.getDrawable()` until the minimum API >= 21. - urlAppBarRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_green)); + if (darkTheme) { + urlAppBarRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_dark_blue)); + } else { + urlAppBarRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_light_green)); + } } else { // The URL we are loading does not have custom domain settings. Load the defaults. // Store the values from `sharedPreferences` in variables. javaScriptEnabled = sharedPreferences.getBoolean("javascript_enabled", false); @@ -2394,9 +2548,6 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation thirdPartyCookiesEnabled = sharedPreferences.getBoolean("third_party_cookies_enabled", false); domStorageEnabled = sharedPreferences.getBoolean("dom_storage_enabled", false); saveFormDataEnabled = sharedPreferences.getBoolean("save_form_data_enabled", false); - String userAgentString = sharedPreferences.getString("user_agent", "PrivacyBrowser/1.0"); - String customUserAgentString = sharedPreferences.getString("custom_user_agent", "PrivacyBrowser/1.0"); - String defaultFontSizeString = sharedPreferences.getString("default_font_size", "100"); // Apply the default settings. mainWebView.getSettings().setJavaScriptEnabled(javaScriptEnabled); @@ -2410,26 +2561,27 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation cookieManager.setAcceptThirdPartyCookies(mainWebView, thirdPartyCookiesEnabled); } - // Set the default user agent. - switch (userAgentString) { - case "WebView default user agent": - // Set the user agent to `""`, which uses the default value. - mainWebView.getSettings().setUserAgentString(""); - break; - - case "Custom user agent": - // Set the custom user agent. - mainWebView.getSettings().setUserAgentString(customUserAgentString); - break; - - default: - // Use the selected user agent. - mainWebView.getSettings().setUserAgentString(userAgentString); - break; + // Only set the user agent if the webpage is not currently loading. Otherwise, changing the user agent on redirects can cause the original website to reload. + if (!urlIsLoading) { + switch (defaultUserAgentString) { + case "WebView default user agent": + // Set the user agent to `""`, which uses the default value. + mainWebView.getSettings().setUserAgentString(""); + break; + + case "Custom user agent": + // Set the custom user agent. + mainWebView.getSettings().setUserAgentString(defaultCustomUserAgentString); + break; + + default: + // Use the selected user agent. + mainWebView.getSettings().setUserAgentString(defaultUserAgentString); + } } // Set a transparent background on `urlTextBox`. We have to use the deprecated `.getDrawable()` until the minimum API >= 21. - urlAppBarRelativeLayout.setBackgroundDrawable(getResources().getDrawable(R.drawable.url_bar_background_transparent)); + urlAppBarRelativeLayout.setBackgroundDrawable(getResources().getDrawable(R.color.transparent)); } // Close `domainsDatabaseHelper`. @@ -2488,23 +2640,39 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation if (firstPartyCookiesEnabled) { // First-party cookies are enabled. firstPartyCookiesIconMenuItem.setIcon(R.drawable.cookies_enabled); } else { // First-party cookies are disabled. - firstPartyCookiesIconMenuItem.setIcon(R.drawable.cookies_disabled); + if (darkTheme) { + firstPartyCookiesIconMenuItem.setIcon(R.drawable.cookies_disabled_dark); + } else { + firstPartyCookiesIconMenuItem.setIcon(R.drawable.cookies_disabled_light); + } } // Update `domStorageIcon`. if (javaScriptEnabled && domStorageEnabled) { // Both JavaScript and DOM storage are enabled. domStorageIconMenuItem.setIcon(R.drawable.dom_storage_enabled); } else if (javaScriptEnabled) { // JavaScript is enabled but DOM storage is disabled. - domStorageIconMenuItem.setIcon(R.drawable.dom_storage_disabled); + if (darkTheme) { + domStorageIconMenuItem.setIcon(R.drawable.dom_storage_disabled_dark); + } else { + domStorageIconMenuItem.setIcon(R.drawable.dom_storage_disabled_light); + } } else { // JavaScript is disabled, so DOM storage is ghosted. - domStorageIconMenuItem.setIcon(R.drawable.dom_storage_ghosted); + if (darkTheme) { + domStorageIconMenuItem.setIcon(R.drawable.dom_storage_ghosted_dark); + } else { + domStorageIconMenuItem.setIcon(R.drawable.dom_storage_ghosted_light); + } } // Update `formDataIcon`. if (saveFormDataEnabled) { // Form data is enabled. formDataIconMenuItem.setIcon(R.drawable.form_data_enabled); } else { // Form data is disabled. - formDataIconMenuItem.setIcon(R.drawable.form_data_disabled); + if (darkTheme) { + formDataIconMenuItem.setIcon(R.drawable.form_data_disabled_dark); + } else { + formDataIconMenuItem.setIcon(R.drawable.form_data_disabled_light); + } } // `invalidateOptionsMenu` calls `onPrepareOptionsMenu()` and redraws the icons in the `AppBar`. `this` references the current activity. @@ -2512,4 +2680,22 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation ActivityCompat.invalidateOptionsMenu(this); } } + + private void highlightUrlText() { + String urlString = urlTextBox.getText().toString(); + + if (urlString.startsWith("http://")) { // Highlight connections that are not encrypted. + urlTextBox.getText().setSpan(redColorSpan, 0, 7, Spanned.SPAN_INCLUSIVE_INCLUSIVE); + } else if (urlString.startsWith("https://")) { // Highlight connections that are encrypted. + urlTextBox.getText().setSpan(initialGrayColorSpan, 0, 8, Spanned.SPAN_INCLUSIVE_INCLUSIVE); + } + + // Get the index of the `/` immediately after the domain name. + int endOfDomainName = urlString.indexOf("/", (urlString.indexOf("//") + 2)); + + // De-emphasize the text after the domain name. + if (endOfDomainName > 0) { + urlTextBox.getText().setSpan(finalGrayColorSpan, endOfDomainName, urlString.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); + } + } }