From: Soren Stoutner Date: Wed, 13 Sep 2017 22:09:05 +0000 (-0700) Subject: Implement a night mode option. https://redmine.stoutner.com/issues/145. X-Git-Tag: v2.6~5 X-Git-Url: https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=commitdiff_plain;h=8ca39b63e2d15fbb6828e255be4e0b5493c744ce Implement a night mode option. https://redmine.stoutner.com/issues/145. --- diff --git a/.idea/dictionaries/soren.xml b/.idea/dictionaries/soren.xml index 25a08015..859d2123 100644 --- a/.idea/dictionaries/soren.xml +++ b/.idea/dictionaries/soren.xml @@ -72,6 +72,7 @@ mitm mozilla navigationview + nightmode nojs oname orbot @@ -112,6 +113,7 @@ subfolders tablayout techrepublic + textarea textview theverge torproject diff --git a/app/src/free/res/layout/main_webview.xml b/app/src/free/res/layout/main_webview.xml index c58d242f..fb80533e 100644 --- a/app/src/free/res/layout/main_webview.xml +++ b/app/src/free/res/layout/main_webview.xml @@ -19,6 +19,7 @@ along with Privacy Browser. If not, see . --> + + tools:showIn="@layout/main_drawerlayout" > + ads:adUnitId="@string/ad_id" > + android:layout_above="@id/adview" > is derived from ic_exit_to_app, which is part of the Android Material icon set and is released under the Apache License 2.0. The full text of the license is below. Modifications copyright © 2017 Soren Stoutner. The resulting image is released under the GPLv3+ license.

+

+ is derived from ic_compare, which is part of the Android Material icon set and is released under the Apache License 2.0. + The full text of the license is below. Modifications copyright © 2017 Soren Stoutner. The resulting image is released under the GPLv3+ license.

orbot is a modified version of the status icon from the Orbot project, which is copyright 2009-2010 Nathan Freitas, The Guardian Project. It is released under the 3-clause BSD license. The full text of the license is below. Modifications copyright © 2017 Soren Stoutner. The resulting image is released under the GPLv3+ license.

diff --git a/app/src/main/assets/en/about_licenses.html b/app/src/main/assets/en/about_licenses.html index 295123d5..54025adb 100644 --- a/app/src/main/assets/en/about_licenses.html +++ b/app/src/main/assets/en/about_licenses.html @@ -58,6 +58,9 @@

is derived from ic_exit_to_app, which is part of the Android Material icon set and is released under the Apache License 2.0. The full text of the license is below. Modifications copyright © 2017 Soren Stoutner. The resulting image is released under the GPLv3+ license.

+

+ is derived from ic_compare, which is part of the Android Material icon set and is released under the Apache License 2.0. + The full text of the license is below. Modifications copyright © 2017 Soren Stoutner. The resulting image is released under the GPLv3+ license.

orbot is a modified version of the status icon from the Orbot project, which is copyright 2009-2010 Nathan Freitas, The Guardian Project. It is released under the 3-clause BSD license. The full text of the license is below. Modifications copyright © 2017 Soren Stoutner. The resulting image is released under the GPLv3+ license.

diff --git a/app/src/main/assets/en/images/night_mode.png b/app/src/main/assets/en/images/night_mode.png new file mode 100644 index 00000000..5e6cf454 Binary files /dev/null and b/app/src/main/assets/en/images/night_mode.png differ diff --git a/app/src/main/assets/es/about_licenses.html b/app/src/main/assets/es/about_licenses.html index 633afd4e..940d643b 100644 --- a/app/src/main/assets/es/about_licenses.html +++ b/app/src/main/assets/es/about_licenses.html @@ -60,8 +60,12 @@ licencia GPLv3+.

deriva de ic_exit_to_app, que es parte del conjunto de iconos Android Material y es liberado bajo la Licencia Apache 2.0. - El texto completo de la licencia se encuentra debajo. Copyright de modificaciones © 2017 Soren Stoutner. La imagen resultante se libera bajo la - licencia GPLv3+.

+ El texto completo de la licencia se encuentra debajo. Copyright de modificaciones © 2017 Soren Stoutner. + La imagen resultante se libera bajo la licencia GPLv3+.

+

+ deriva de ic_exit_to_app, que es parte del conjunto de iconos Android Material y es liberado bajo la Licencia Apache 2.0. + El texto completo de la licencia se encuentra debajo. Copyright de modificaciones © 2017 Soren Stoutner. + La imagen resultante se libera bajo la licencia GPLv3+.

orbot es una versión modificada del icono de estado del proyecto Orbot, que tiene copyright 2009-2010 por Nathan Freitas, The Guardian Project. Es liberado bajo la licencia BSD modificada (de 3 cláusulas). El texto completo de la licencia se encuentra debajo. Copyright de modificaciones © 2017 Soren Stoutner. diff --git a/app/src/main/assets/it/about_licenses.html b/app/src/main/assets/it/about_licenses.html index 7e389967..77986b30 100644 --- a/app/src/main/assets/it/about_licenses.html +++ b/app/src/main/assets/it/about_licenses.html @@ -66,6 +66,10 @@ è stata derivata da ic_exit_to_app, che fa parte dell'Android Material icon set ed è stata rilasciata sotto Licenza Apache 2.0. Il testo completo della licenza è riportato di seguito. Copyright delle modifiche © 2017 Soren Stoutner. L'immagine risultante è rilasciata sotto Licenza GPLv3+.

+

+ è stata derivata da ic_compare, che fa parte dell'Android Material icon set ed è stata rilasciata sotto Licenza Apache 2.0. + Il testo completo della licenza è riportato di seguito. Copyright delle modifiche © 2017 Soren Stoutner. + L'immagine risultante è rilasciata sotto Licenza GPLv3+.

orbot è una versione modificata della icona di stato del progetto Orbot, il cui copyright è 2009-2010 Nathan Freitas, The Guardian Project. E' rilasciata sotto 3-clause BSD license. Il testo completo della Licenza è riportato di seguito. Copyright delle modifiche © 2017Soren Stoutner. L'immagine risultante è rilasciata sotto Licenza GPLv3+.

diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/DomainsActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/DomainsActivity.java index f71a1384..de68623e 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/DomainsActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/DomainsActivity.java @@ -520,6 +520,7 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo EditText customUserAgentEditText = (EditText) findViewById(R.id.domain_settings_custom_user_agent_edittext); Spinner fontSizeSpinner = (Spinner) findViewById(R.id.domain_settings_font_size_spinner); Spinner displayWebpageImagesSpinner = (Spinner) findViewById(R.id.domain_settings_display_webpage_images_spinner); + Spinner nightModeSpinner = (Spinner) findViewById(R.id.domain_settings_night_mode_spinner); Switch pinnedSslCertificateSwitch = (Switch) findViewById(R.id.domain_settings_pinned_ssl_certificate_switch); RadioButton savedSslCertificateRadioButton = (RadioButton) findViewById(R.id.saved_ssl_certificate_radiobutton); RadioButton currentWebsiteCertificateRadioButton = (RadioButton) findViewById(R.id.current_website_certificate_radiobutton); @@ -534,6 +535,7 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo int userAgentPositionInt = userAgentSpinner.getSelectedItemPosition(); int fontSizePositionInt = fontSizeSpinner.getSelectedItemPosition(); int displayWebpageImagesInt = displayWebpageImagesSpinner.getSelectedItemPosition(); + int nightModeInt = nightModeSpinner.getSelectedItemPosition(); boolean pinnedSslCertificate = pinnedSslCertificateSwitch.isChecked(); // Get the data for the `Spinners` from the entry values string arrays. @@ -550,7 +552,7 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo if (savedSslCertificateRadioButton.isChecked()) { // The current certificate is being used. // Update the database except for the certificate. domainsDatabaseHelper.updateDomainExceptCertificate(DomainsActivity.currentDomainDatabaseId, domainNameString, javaScriptEnabledBoolean, firstPartyCookiesEnabledBoolean, thirdPartyCookiesEnabledBoolean, domStorageEnabledEnabledBoolean, - formDataEnabledBoolean, userAgentString, fontSizeInt, displayWebpageImagesInt, pinnedSslCertificate); + formDataEnabledBoolean, userAgentString, fontSizeInt, displayWebpageImagesInt, nightModeInt, pinnedSslCertificate); } else if (currentWebsiteCertificateRadioButton.isChecked()) { // The certificate is being updated with the current website certificate. // Get the current website SSL certificate. SslCertificate currentWebsiteSslCertificate = MainWebViewActivity.sslCertificate; @@ -567,12 +569,12 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo // Update the database. domainsDatabaseHelper.updateDomainWithCertificate(currentDomainDatabaseId, domainNameString, javaScriptEnabledBoolean, firstPartyCookiesEnabledBoolean, thirdPartyCookiesEnabledBoolean, domStorageEnabledEnabledBoolean, formDataEnabledBoolean, - userAgentString, fontSizeInt, displayWebpageImagesInt, pinnedSslCertificate, issuedToCommonName, issuedToOrganization, issuedToOrganizationalUnit, issuedByCommonName, issuedByOrganization, issuedByOrganizationalUnit, startDateLong, - endDateLong); + userAgentString, fontSizeInt, displayWebpageImagesInt, nightModeInt, pinnedSslCertificate, issuedToCommonName, issuedToOrganization, issuedToOrganizationalUnit, issuedByCommonName, issuedByOrganization, issuedByOrganizationalUnit, + startDateLong, endDateLong); } else { // No certificate is selected. // Update the database, with PINNED_SSL_CERTIFICATE set to false. domainsDatabaseHelper.updateDomainExceptCertificate(currentDomainDatabaseId, domainNameString, javaScriptEnabledBoolean, firstPartyCookiesEnabledBoolean, thirdPartyCookiesEnabledBoolean, domStorageEnabledEnabledBoolean, formDataEnabledBoolean, - userAgentString, fontSizeInt, displayWebpageImagesInt, false); + userAgentString, fontSizeInt, displayWebpageImagesInt, nightModeInt, false); } } 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 b13a7852..02ac2567 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java @@ -41,6 +41,7 @@ import android.net.http.SslCertificate; import android.net.http.SslError; import android.os.Build; import android.os.Bundle; +import android.os.Handler; import android.preference.PreferenceManager; import android.print.PrintDocumentAdapter; import android.print.PrintManager; @@ -151,8 +152,11 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation // `displayWebpageImagesBoolean` is public static so it can be accessed from `DomainSettingsFragment`. It is also used in `applyAppSettings()` and `applyDomainSettings()`. public static boolean displayWebpageImagesBoolean; - // `reloadOnRestartBoolean` is public static so it can be accessed from `SettingsFragment`. It is also used in `onRestart()` - public static boolean reloadOnRestartBoolean; + // `reloadOnRestart` is public static so it can be accessed from `SettingsFragment`. It is also used in `onRestart()` + public static boolean reloadOnRestart; + + // `reloadUrlOnRestart` is public static so it can be accessed from `SettingsFragment`. It is also used in `onRestart()`. + public static boolean loadUrlOnRestart; // The pinned domain SSL Certificate variables are public static so they can be accessed from `PinnedSslCertificateMismatchDialog`. They are also used in `onCreate()` and `applyDomainSettings()`. public static int domainSettingsDatabaseId; @@ -203,22 +207,24 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation // `customHeader` is used in `onCreate()`, `onOptionsItemSelected()`, `onCreateContextMenu()`, and `loadUrl()`. private final Map customHeaders = new HashMap<>(); - // `javaScriptEnabled` is also used in `onCreate()`, `onCreateOptionsMenu()`, `onOptionsItemSelected()`, `loadUrlFromTextBox()`, and `applyAppSettings()`. - // It is `Boolean` instead of `boolean` because `applyAppSettings()` needs to know if it is `null`. - private Boolean javaScriptEnabled; + // `javaScriptEnabled` is also used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, `applyDomainSettings()`, and `updatePrivacyIcons()`. + private boolean javaScriptEnabled; - // `firstPartyCookiesEnabled` is used in `onCreate()`, `onCreateOptionsMenu()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, `onDownloadImage()`, `onDownloadFile()`, and `applyAppSettings()`. + // `firstPartyCookiesEnabled` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, `onDownloadImage()`, `onDownloadFile()`, and `applyDomainSettings()`. private boolean firstPartyCookiesEnabled; - // `thirdPartyCookiesEnabled` used in `onCreate()`, `onCreateOptionsMenu()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyAppSettings()`. + // `thirdPartyCookiesEnabled` used in `onCreate()`, `onPrepareOptionsMenu()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyDomainSettings()`. private boolean thirdPartyCookiesEnabled; - // `domStorageEnabled` is used in `onCreate()`, `onCreateOptionsMenu()`, `onOptionsItemSelected()`, and `applyAppSettings()`. + // `domStorageEnabled` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyDomainSettings()`. private boolean domStorageEnabled; - // `saveFormDataEnabled` is used in `onCreate()`, `onCreateOptionsMenu()`, `onOptionsItemSelected()`, and `applyAppSettings()`. + // `saveFormDataEnabled` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyDomainSettings()`. private boolean saveFormDataEnabled; + // `nightMode` is used in `onCreate()` and `applyDomainSettings()`. + private boolean nightMode; + // `swipeToRefreshEnabled` is used in `onPrepareOptionsMenu()` and `applyAppSettings()`. private boolean swipeToRefreshEnabled; @@ -749,6 +755,11 @@ 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) { + // If night mode is enabled, hide `mainWebView` until after the night mode CSS is applied. + if (nightMode) { + mainWebView.setVisibility(View.INVISIBLE); + } + // 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. @@ -770,7 +781,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation } } - // Update formattedUrlString and urlTextBox. It is necessary to do this after the page finishes loading because the final URL can change during load. + // It is necessary to update `formattedUrlString` and `urlTextBox` 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. @@ -933,10 +944,36 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation public void onProgressChanged(WebView view, int progress) { progressBar.setProgress(progress); if (progress < 100) { + // Show the progress bar. progressBar.setVisibility(View.VISIBLE); } else { + // Hide the progress bar. progressBar.setVisibility(View.GONE); + // Inject the night mode CSS if night mode is enabled. + if (nightMode) { + // `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. `!important` takes precedent over any existing sub-settings. + mainWebView.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;}" + + "'; parent.appendChild(style)})()", null); + } + + // Initialize a `Handler` to display `mainWebView`, which may have been hid by a night mode domain setting even if night mode is not currently enabled. + Handler displayWebViewHandler = new Handler(); + + // Setup a `Runnable` to display `mainWebView` after a delay to allow the CSS to be applied. + Runnable displayWebViewRunnable = new Runnable() { + public void run() { + mainWebView.setVisibility(View.VISIBLE); + } + }; + + // Use `displayWebViewHandler` to delay the displaying of `mainWebView` for 1000 milliseconds. + displayWebViewHandler.postDelayed(displayWebViewRunnable, 1000); + //Stop the `SwipeToRefresh` indicator if it is running swipeRefreshLayout.setRefreshing(false); } @@ -1079,6 +1116,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation thirdPartyCookiesEnabled = false; domStorageEnabled = false; saveFormDataEnabled = false; + nightMode = false; // Initialize `webViewTitle`. webViewTitle = getString(R.string.no_title); @@ -1139,12 +1177,21 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation setDisplayWebpageImages(); // Reload the webpage if displaying of images has been disabled in `SettingsFragment`. - if (reloadOnRestartBoolean) { + if (reloadOnRestart) { // Reload `mainWebView`. mainWebView.reload(); // Reset `reloadOnRestartBoolean`. - reloadOnRestartBoolean = false; + reloadOnRestart = false; + } + + // Load the URL on restart to apply changes to night mode. + if (loadUrlOnRestart) { + // Load the current `formattedUrlString`. + loadUrl(formattedUrlString); + + // Reset `loadUrlOnRestart. + loadUrlOnRestart = false; } } @@ -2619,10 +2666,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. + // Store the general preference 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"); + nightMode = sharedPreferences.getBoolean("night_mode", false); 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. @@ -2639,6 +2687,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation String userAgentString = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.USER_AGENT)); int fontSize = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.FONT_SIZE)); displayWebpageImagesInt = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.DISPLAY_IMAGES)); + int nightModeInt = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.NIGHT_MODE)); pinnedDomainSslCertificate = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE)) == 1); pinnedDomainSslIssuedToCNameString = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME)); pinnedDomainSslIssuedToONameString = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION)); @@ -2647,6 +2696,22 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation pinnedDomainSslIssuedByONameString = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION)); pinnedDomainSslIssuedByUNameString = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT)); + // Set `nightMode` according to `nightModeInt`. If `nightModeInt` is `DomainsDatabaseHelper.NIGHT_MODE_SYSTEM_DEFAULT` the current setting from `sharedPreferences` will be used. + switch (nightModeInt) { + case DomainsDatabaseHelper.NIGHT_MODE_ENABLED: + nightMode = true; + break; + + case DomainsDatabaseHelper.NIGHT_MODE_DISABLED: + nightMode = false; + break; + } + + // Set `javaScriptEnabled` to be `true` if `night_mode` is `true`. + if (nightMode) { + javaScriptEnabled = true; + } + // Set the pinned SSL certificate start date to `null` if the saved date `long` is 0. if (currentHostDomainSettingsCursor.getLong(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE)) == 0) { pinnedDomainSslStartDate = null; @@ -2729,6 +2794,11 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation domStorageEnabled = sharedPreferences.getBoolean("dom_storage_enabled", false); saveFormDataEnabled = sharedPreferences.getBoolean("save_form_data_enabled", false); + // Set `javaScriptEnabled` to be `true` if `night_mode` is `true`. + if (nightMode) { + javaScriptEnabled = true; + } + // Apply the default settings. mainWebView.getSettings().setJavaScriptEnabled(javaScriptEnabled); cookieManager.setAcceptCookie(firstPartyCookiesEnabled); diff --git a/app/src/main/java/com/stoutner/privacybrowser/fragments/DomainSettingsFragment.java b/app/src/main/java/com/stoutner/privacybrowser/fragments/DomainSettingsFragment.java index 3889ef7f..31dd0278 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/fragments/DomainSettingsFragment.java +++ b/app/src/main/java/com/stoutner/privacybrowser/fragments/DomainSettingsFragment.java @@ -87,15 +87,16 @@ public class DomainSettingsFragment extends Fragment { // Get a handle for the shared preference. SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); - // Store the default user agent string values. + // Store the default settings. final String defaultUserAgentString = sharedPreferences.getString("user_agent", "PrivacyBrowser/1.0"); final String defaultCustomUserAgentString = sharedPreferences.getString("custom_user_agent", "PrivacyBrowser/1.0"); String defaultFontSizeString = sharedPreferences.getString("default_font_size", "100"); - boolean defaultDisplayWebpageImagesBoolean = sharedPreferences.getBoolean("display_website_images", true); + final boolean defaultDisplayWebpageImagesBoolean = sharedPreferences.getBoolean("display_website_images", true); + final boolean defaultNightModeBoolean = sharedPreferences.getBoolean("night_mode", false); // Get handles for the views in the fragment. final EditText domainNameEditText = (EditText) domainSettingsView.findViewById(R.id.domain_settings_name_edittext); - Switch javaScriptEnabledSwitch = (Switch) domainSettingsView.findViewById(R.id.domain_settings_javascript_switch); + final Switch javaScriptEnabledSwitch = (Switch) domainSettingsView.findViewById(R.id.domain_settings_javascript_switch); final ImageView javaScriptImageView = (ImageView) domainSettingsView.findViewById(R.id.domain_settings_javascript_imageview); Switch firstPartyCookiesEnabledSwitch = (Switch) domainSettingsView.findViewById(R.id.domain_settings_first_party_cookies_switch); final ImageView firstPartyCookiesImageView = (ImageView) domainSettingsView.findViewById(R.id.domain_settings_first_party_cookies_imageview); @@ -106,14 +107,17 @@ public class DomainSettingsFragment extends Fragment { final ImageView domStorageImageView = (ImageView) domainSettingsView.findViewById(R.id.domain_settings_dom_storage_imageview); Switch formDataEnabledSwitch = (Switch) domainSettingsView.findViewById(R.id.domain_settings_form_data_switch); final ImageView formDataImageView = (ImageView) domainSettingsView.findViewById(R.id.domain_settings_form_data_imageview); - Spinner userAgentSpinner = (Spinner) domainSettingsView.findViewById(R.id.domain_settings_user_agent_spinner); + final Spinner userAgentSpinner = (Spinner) domainSettingsView.findViewById(R.id.domain_settings_user_agent_spinner); final TextView userAgentTextView = (TextView) domainSettingsView.findViewById(R.id.domain_settings_user_agent_textview); final EditText customUserAgentEditText = (EditText) domainSettingsView.findViewById(R.id.domain_settings_custom_user_agent_edittext); - Spinner fontSizeSpinner = (Spinner) domainSettingsView.findViewById(R.id.domain_settings_font_size_spinner); + final Spinner fontSizeSpinner = (Spinner) domainSettingsView.findViewById(R.id.domain_settings_font_size_spinner); final TextView fontSizeTextView = (TextView) domainSettingsView.findViewById(R.id.domain_settings_font_size_textview); final ImageView displayWebpageImagesImageView = (ImageView) domainSettingsView.findViewById(R.id.domain_settings_display_webpage_images_imageview); - Spinner displayWebpageImagesSpinner = (Spinner) domainSettingsView.findViewById(R.id.domain_settings_display_webpage_images_spinner); + final Spinner displayWebpageImagesSpinner = (Spinner) domainSettingsView.findViewById(R.id.domain_settings_display_webpage_images_spinner); final TextView displayImagesTextView = (TextView) domainSettingsView.findViewById(R.id.domain_settings_display_webpage_images_textview); + final ImageView nightModeImageView = (ImageView) domainSettingsView.findViewById(R.id.domain_settings_night_mode_imageview); + final Spinner nightModeSpinner = (Spinner) domainSettingsView.findViewById(R.id.domain_settings_night_mode_spinner); + final TextView nightModeTextView = (TextView) domainSettingsView.findViewById(R.id.domain_settings_night_mode_textview); final ImageView pinnedSslCertificateImageView = (ImageView) domainSettingsView.findViewById(R.id.domain_settings_pinned_ssl_certificate_imageview); Switch pinnedSslCertificateSwitch = (Switch) domainSettingsView.findViewById(R.id.domain_settings_pinned_ssl_certificate_switch); final LinearLayout savedSslCertificateLinearLayout = (LinearLayout) domainSettingsView.findViewById(R.id.saved_ssl_certificate_linearlayout); @@ -157,14 +161,15 @@ public class DomainSettingsFragment extends Fragment { // Save the `Cursor` entries as variables. String domainNameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.DOMAIN_NAME)); - int javaScriptEnabledInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_JAVASCRIPT)); + final int javaScriptEnabledInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_JAVASCRIPT)); int firstPartyCookiesEnabledInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FIRST_PARTY_COOKIES)); int thirdPartyCookiesEnabledInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_THIRD_PARTY_COOKIES)); - int domStorageEnabledInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_DOM_STORAGE)); + final int domStorageEnabledInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_DOM_STORAGE)); int formDataEnabledInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FORM_DATA)); final String currentUserAgentString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.USER_AGENT)); int fontSizeInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.FONT_SIZE)); int displayImagesInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.DISPLAY_IMAGES)); + int nightModeInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.NIGHT_MODE)); int pinnedSslCertificateInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE)); final String savedSslCertificateIssuedToCNameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME)); String savedSslCertificateIssuedToONameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION)); @@ -191,17 +196,20 @@ public class DomainSettingsFragment extends Fragment { final ArrayAdapter userAgentEntryValuesArrayAdapter = ArrayAdapter.createFromResource(context, R.array.domain_settings_user_agent_entry_values, R.layout.spinner_item); ArrayAdapter fontSizeArrayAdapter = ArrayAdapter.createFromResource(context, R.array.domain_settings_font_size_entries, R.layout.spinner_item); ArrayAdapter fontSizeEntryValuesArrayAdapter = ArrayAdapter.createFromResource(context, R.array.domain_settings_font_size_entry_values, R.layout.spinner_item); - final ArrayAdapter displayImagesArrayAdapter = ArrayAdapter.createFromResource(context, R.array.display_website_images_array, R.layout.spinner_item); + final ArrayAdapter displayImagesArrayAdapter = ArrayAdapter.createFromResource(context, R.array.display_webpage_images_array, R.layout.spinner_item); + ArrayAdapter nightModeArrayAdapter = ArrayAdapter.createFromResource(context, R.array.night_mode_array, R.layout.spinner_item); // Set the `DropDownViewResource` on the `Spinners`. userAgentArrayAdapter.setDropDownViewResource(R.layout.spinner_dropdown_item); fontSizeArrayAdapter.setDropDownViewResource(R.layout.spinner_dropdown_item); displayImagesArrayAdapter.setDropDownViewResource(R.layout.spinner_dropdown_item); + nightModeArrayAdapter.setDropDownViewResource(R.layout.spinner_dropdown_item); // Set the `ArrayAdapters` for the `Spinners`. userAgentSpinner.setAdapter(userAgentArrayAdapter); fontSizeSpinner.setAdapter(fontSizeArrayAdapter); displayWebpageImagesSpinner.setAdapter(displayImagesArrayAdapter); + nightModeSpinner.setAdapter(nightModeArrayAdapter); // Create a `SpannableStringBuilder` for each `TextView` that needs multiple colors of text. SpannableStringBuilder savedSslCertificateIssuedToCNameStringBuilder = new SpannableStringBuilder(cNameLabel + savedSslCertificateIssuedToCNameString); @@ -303,13 +311,28 @@ public class DomainSettingsFragment extends Fragment { } }); - // Set the JavaScript status. + // Create a `boolean` to track if night mode is enabled. + boolean nightModeEnabled = (nightModeInt == DomainsDatabaseHelper.NIGHT_MODE_ENABLED) || ((nightModeInt == DomainsDatabaseHelper.NIGHT_MODE_SYSTEM_DEFAULT) && defaultNightModeBoolean); + + // Disable the JavaScript `Switch` if night mode is enabled. + if (nightModeEnabled) { + javaScriptEnabledSwitch.setEnabled(false); + } else { + javaScriptEnabledSwitch.setEnabled(true); + } + + // Set the JavaScript icon. + if ((javaScriptEnabledInt == 1) || nightModeEnabled) { + javaScriptImageView.setImageDrawable(resources.getDrawable(R.drawable.javascript_enabled)); + } else { + javaScriptImageView.setImageDrawable(resources.getDrawable(R.drawable.privacy_mode)); + } + + // Set the JavaScript `Switch` status. if (javaScriptEnabledInt == 1) { // JavaScript is enabled. javaScriptEnabledSwitch.setChecked(true); - javaScriptImageView.setImageDrawable(resources.getDrawable(R.drawable.javascript_enabled)); } else { // JavaScript is disabled. javaScriptEnabledSwitch.setChecked(false); - javaScriptImageView.setImageDrawable(resources.getDrawable(R.drawable.privacy_mode)); } // Set the first-party cookies status. Once minimum API >= 21 we can use a selector as the tint mode instead of specifying different icons. @@ -369,7 +392,10 @@ public class DomainSettingsFragment extends Fragment { } // Only enable DOM storage if JavaScript is enabled. - if (javaScriptEnabledInt == 1) { // JavaScript is enabled. + if ((javaScriptEnabledInt == 1) || nightModeEnabled) { // JavaScript is enabled. + // Enable the DOM storage `Switch`. + domStorageEnabledSwitch.setEnabled(true); + // Set the DOM storage status. Once minimum API >= 21 we can use a selector as the tint mode instead of specifying different icons. if (domStorageEnabledInt == 1) { // Both JavaScript and DOM storage are enabled. domStorageEnabledSwitch.setChecked(true); @@ -386,6 +412,9 @@ public class DomainSettingsFragment extends Fragment { } } } else { // JavaScript is disabled. + // Disable the DOM storage `Switch`. + domStorageEnabledSwitch.setEnabled(false); + // Set the checked status of DOM storage. if (domStorageEnabledInt == 1) { // DOM storage is enabled but JavaScript is disabled. domStorageEnabledSwitch.setChecked(true); @@ -393,9 +422,6 @@ public class DomainSettingsFragment extends Fragment { domStorageEnabledSwitch.setChecked(false); } - // Disable `domStorageEnabledSwitch`. - domStorageEnabledSwitch.setEnabled(false); - // Set the icon according to the theme. if (MainWebViewActivity.darkTheme) { domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_ghosted_dark)); @@ -482,6 +508,15 @@ public class DomainSettingsFragment extends Fragment { } } + // Open the user agent spinner when the `TextView` is clicked. + userAgentTextView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + // Open the user agent spinner. + userAgentSpinner.performClick(); + } + }); + // Set the selected font size. int fontSizeArrayPosition = fontSizeEntryValuesArrayAdapter.getPosition(String.valueOf(fontSizeInt)); fontSizeSpinner.setSelection(fontSizeArrayPosition); @@ -497,27 +532,36 @@ public class DomainSettingsFragment extends Fragment { fontSizeTextView.setVisibility(View.GONE); } - // Set the selected display website images mode. + // Open the font size spinner when the `TextView` is clicked. + fontSizeTextView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + // Open the user agent spinner. + fontSizeSpinner.performClick(); + } + }); + + // Display the website images mode in the spinner. displayWebpageImagesSpinner.setSelection(displayImagesInt); // Set the default display images text. if (defaultDisplayWebpageImagesBoolean) { - displayImagesTextView.setText(displayImagesArrayAdapter.getItem(1)); + displayImagesTextView.setText(displayImagesArrayAdapter.getItem(DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_ENABLED)); } else { - displayImagesTextView.setText(displayImagesArrayAdapter.getItem(2)); + displayImagesTextView.setText(displayImagesArrayAdapter.getItem(DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_DISABLED)); } - // Set the display website images icon and `TextView` settings. + // Set the display website images icon and `TextView` settings. Once minimum API >= 21 we can use a selector as the tint mode instead of specifying different icons. switch (displayImagesInt) { case DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_SYSTEM_DEFAULT: - if (MainWebViewActivity.displayWebpageImagesBoolean) { + if (defaultDisplayWebpageImagesBoolean) { // Display webpage images enabled by default. // Set the icon according to the theme. if (MainWebViewActivity.darkTheme) { displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_dark)); } else { displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_light)); } - } else { + } else { // Display webpage images disabled by default. // Set the icon according to the theme. if (MainWebViewActivity.darkTheme) { displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_disabled_dark)); @@ -554,9 +598,85 @@ public class DomainSettingsFragment extends Fragment { displayImagesTextView.setVisibility(View.GONE); break; } + + // Open the display images spinner when the `TextView` is clicked. + displayImagesTextView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + // Open the user agent spinner. + displayWebpageImagesSpinner.performClick(); + } + }); + + // Display the night mode in the spinner. + nightModeSpinner.setSelection(nightModeInt); + + // Set the default night mode text. + if (defaultNightModeBoolean) { + nightModeTextView.setText(nightModeArrayAdapter.getItem(DomainsDatabaseHelper.NIGHT_MODE_ENABLED)); + } else { + nightModeTextView.setText(nightModeArrayAdapter.getItem(DomainsDatabaseHelper.NIGHT_MODE_DISABLED)); + } + + // Set the night mode icon and `TextView` settings. Once minimum API >= 21 we can use a selector as the tint mode instead of specifying different icons. + switch (displayImagesInt) { + case DomainsDatabaseHelper.NIGHT_MODE_SYSTEM_DEFAULT: + if (defaultNightModeBoolean) { // Night mode enabled by default. + // Set the icon according to the theme. + if (MainWebViewActivity.darkTheme) { + nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_enabled_dark)); + } else { + nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_enabled_light)); + } + } else { // Night mode disabled by default. + // Set the icon according to the theme. + if (MainWebViewActivity.darkTheme) { + nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_disabled_dark)); + } else { + nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_disabled_light)); + } + } + + // Show `nightModeTextView`. + nightModeTextView.setVisibility(View.VISIBLE); + break; + + case DomainsDatabaseHelper.NIGHT_MODE_ENABLED: + // Set the icon according to the theme. + if (MainWebViewActivity.darkTheme) { + nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_enabled_dark)); + } else { + nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_enabled_light)); + } + + // Hide `nightModeTextView`. + nightModeTextView.setVisibility(View.GONE); + break; + + case DomainsDatabaseHelper.NIGHT_MODE_DISABLED: + // Set the icon according to the theme. + if (MainWebViewActivity.darkTheme) { + nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_disabled_dark)); + } else { + nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_disabled_light)); + } + + // Hide `nightModeTextView`. + nightModeTextView.setVisibility(View.GONE); + break; + } + + // Open the night mode spinner when the `TextView` is clicked. + nightModeTextView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + // Open the user agent spinner. + nightModeSpinner.performClick(); + } + }); // Set the pinned SSL certificate icon. - if (pinnedSslCertificateInt == 1) { // Pinned SSL certificate is enabled. + if (pinnedSslCertificateInt == 1) { // Pinned SSL certificate is enabled. Once minimum API >= 21 we can use a selector as the tint mode instead of specifying different icons. // Check the switch. pinnedSslCertificateSwitch.setChecked(true); @@ -962,7 +1082,7 @@ public class DomainSettingsFragment extends Fragment { // Update the icon and the visibility of `displayImagesTextView`. switch (position) { case DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_SYSTEM_DEFAULT: - if (MainWebViewActivity.displayWebpageImagesBoolean) { + if (defaultDisplayWebpageImagesBoolean) { // Set the icon according to the theme. if (MainWebViewActivity.darkTheme) { displayWebpageImagesImageView.setImageDrawable(resources.getDrawable(R.drawable.images_enabled_dark)); @@ -1013,6 +1133,121 @@ public class DomainSettingsFragment extends Fragment { // Do nothing. } }); + + // Set the `nightModeSpinner` `onItemSelectedListener()`. + nightModeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + // Update the icon and the visibility of `nightModeTextView`. Once minimum API >= 21 we can use a selector as the tint mode instead of specifying different icons. + switch (position) { + case DomainsDatabaseHelper.NIGHT_MODE_SYSTEM_DEFAULT: + if (defaultNightModeBoolean) { // Night mode enabled by default. + // Set the icon according to the theme. + if (MainWebViewActivity.darkTheme) { + nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_enabled_dark)); + } else { + nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_enabled_light)); + } + } else { // Night mode disabled by default. + // Set the icon according to the theme. + if (MainWebViewActivity.darkTheme) { + nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_disabled_dark)); + } else { + nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_disabled_light)); + } + } + + // Show `nightModeTextView`. + nightModeTextView.setVisibility(View.VISIBLE); + break; + + case DomainsDatabaseHelper.NIGHT_MODE_ENABLED: + // Set the icon according to the theme. + if (MainWebViewActivity.darkTheme) { + nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_enabled_dark)); + } else { + nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_enabled_light)); + } + + // Hide `nightModeTextView`. + nightModeTextView.setVisibility(View.GONE); + break; + + case DomainsDatabaseHelper.NIGHT_MODE_DISABLED: + // Set the icon according to the theme. + if (MainWebViewActivity.darkTheme) { + nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_disabled_dark)); + } else { + nightModeImageView.setImageDrawable(resources.getDrawable(R.drawable.night_mode_disabled_light)); + } + + // Hide `nightModeTextView`. + nightModeTextView.setVisibility(View.GONE); + break; + } + + // Create a `boolean` to store the current night mode setting. + boolean currentNightModeEnabled = (position == DomainsDatabaseHelper.NIGHT_MODE_ENABLED) || ((position == DomainsDatabaseHelper.NIGHT_MODE_SYSTEM_DEFAULT) && defaultNightModeBoolean); + + // Disable the JavaScript `Switch` if night mode is enabled. + if (currentNightModeEnabled) { + javaScriptEnabledSwitch.setEnabled(false); + } else { + javaScriptEnabledSwitch.setEnabled(true); + } + + // Update the JavaScript icon. + if ((javaScriptEnabledInt == 1) || currentNightModeEnabled) { + javaScriptImageView.setImageDrawable(resources.getDrawable(R.drawable.javascript_enabled)); + } else { + javaScriptImageView.setImageDrawable(resources.getDrawable(R.drawable.privacy_mode)); + } + + // Update the DOM storage status. + if ((javaScriptEnabledInt == 1) || currentNightModeEnabled) { // JavaScript is enabled. + // Enable the DOM storage `Switch`. + domStorageEnabledSwitch.setEnabled(true); + + // Set the DOM storage status. Once minimum API >= 21 we can use a selector as the tint mode instead of specifying different icons. + if (domStorageEnabledInt == 1) { // Both JavaScript and DOM storage are enabled. + domStorageEnabledSwitch.setChecked(true); + domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_enabled)); + } else { // JavaScript is enabled but DOM storage is disabled. + // Set the DOM storage switch to off. + domStorageEnabledSwitch.setChecked(false); + + // Set the icon according to the theme. + if (MainWebViewActivity.darkTheme) { + domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_dark)); + } else { + domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_disabled_light)); + } + } + } else { // JavaScript is disabled. + // Disable the DOM storage `Switch`. + domStorageEnabledSwitch.setEnabled(false); + + // Set the checked status of DOM storage. + if (domStorageEnabledInt == 1) { // DOM storage is enabled but JavaScript is disabled. + domStorageEnabledSwitch.setChecked(true); + } else { // Both JavaScript and DOM storage are disabled. + domStorageEnabledSwitch.setChecked(false); + } + + // Set the icon according to the theme. + if (MainWebViewActivity.darkTheme) { + domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_ghosted_dark)); + } else { + domStorageImageView.setImageDrawable(resources.getDrawable(R.drawable.dom_storage_ghosted_light)); + } + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + // Do nothing. + } + }); // Set the `pinnedSSLCertificateSwitch` `onCheckedChangeListener()`. pinnedSslCertificateSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { diff --git a/app/src/main/java/com/stoutner/privacybrowser/fragments/DomainsListFragment.java b/app/src/main/java/com/stoutner/privacybrowser/fragments/DomainsListFragment.java index 34349d79..172a6438 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/fragments/DomainsListFragment.java +++ b/app/src/main/java/com/stoutner/privacybrowser/fragments/DomainsListFragment.java @@ -80,6 +80,7 @@ public class DomainsListFragment extends Fragment { EditText customUserAgentEditText = (EditText) domainSettingsFragmentView.findViewById(R.id.domain_settings_custom_user_agent_edittext); Spinner fontSizeSpinner = (Spinner) domainSettingsFragmentView.findViewById(R.id.domain_settings_font_size_spinner); Spinner displayWebpageImagesSpinner = (Spinner) domainSettingsFragmentView.findViewById(R.id.domain_settings_display_webpage_images_spinner); + Spinner nightModeSpinner = (Spinner) domainSettingsFragmentView.findViewById(R.id.domain_settings_night_mode_spinner); Switch pinnedSslCertificateSwitch = (Switch) domainSettingsFragmentView.findViewById(R.id.domain_settings_pinned_ssl_certificate_switch); RadioButton savedSslCertificateRadioButton = (RadioButton) domainSettingsFragmentView.findViewById(R.id.saved_ssl_certificate_radiobutton); RadioButton currentWebsiteCertificateRadioButton = (RadioButton) domainSettingsFragmentView.findViewById(R.id.current_website_certificate_radiobutton); @@ -94,6 +95,7 @@ public class DomainsListFragment extends Fragment { int userAgentPositionInt = userAgentSpinner.getSelectedItemPosition(); int fontSizePositionInt = fontSizeSpinner.getSelectedItemPosition(); int displayWebpageImagesInt = displayWebpageImagesSpinner.getSelectedItemPosition(); + int nightModeInt = nightModeSpinner.getSelectedItemPosition(); boolean pinnedSslCertificate = pinnedSslCertificateSwitch.isChecked(); // Get the data for the `Spinners` from the entry values string arrays. @@ -110,7 +112,7 @@ public class DomainsListFragment extends Fragment { if (savedSslCertificateRadioButton.isChecked()) { // The current certificate is being used. // Update the database except for the certificate. domainsDatabaseHelper.updateDomainExceptCertificate(DomainsActivity.currentDomainDatabaseId, domainNameString, javaScriptEnabledBoolean, firstPartyCookiesEnabledBoolean, thirdPartyCookiesEnabledBoolean, domStorageEnabledEnabledBoolean, - formDataEnabledBoolean, userAgentString, fontSizeInt, displayWebpageImagesInt, pinnedSslCertificate); + formDataEnabledBoolean, userAgentString, fontSizeInt, displayWebpageImagesInt, nightModeInt, pinnedSslCertificate); } else if (currentWebsiteCertificateRadioButton.isChecked()) { // The certificate is being updated with the current website certificate. // Get the current website SSL certificate. SslCertificate currentWebsiteSslCertificate = MainWebViewActivity.sslCertificate; @@ -127,12 +129,12 @@ public class DomainsListFragment extends Fragment { // Update the database. domainsDatabaseHelper.updateDomainWithCertificate(DomainsActivity.currentDomainDatabaseId, domainNameString, javaScriptEnabledBoolean, firstPartyCookiesEnabledBoolean, thirdPartyCookiesEnabledBoolean, domStorageEnabledEnabledBoolean, - formDataEnabledBoolean, userAgentString, fontSizeInt, displayWebpageImagesInt, pinnedSslCertificate, issuedToCommonName, issuedToOrganization, issuedToOrganizationalUnit, issuedByCommonName, issuedByOrganization, + formDataEnabledBoolean, userAgentString, fontSizeInt, displayWebpageImagesInt, nightModeInt, pinnedSslCertificate, issuedToCommonName, issuedToOrganization, issuedToOrganizationalUnit, issuedByCommonName, issuedByOrganization, issuedByOrganizationalUnit, startDateLong, endDateLong); } else { // No certificate is selected. // Update the database, with PINNED_SSL_CERTIFICATE set to false. domainsDatabaseHelper.updateDomainExceptCertificate(DomainsActivity.currentDomainDatabaseId, domainNameString, javaScriptEnabledBoolean, firstPartyCookiesEnabledBoolean, thirdPartyCookiesEnabledBoolean, domStorageEnabledEnabledBoolean, - formDataEnabledBoolean, userAgentString, fontSizeInt, displayWebpageImagesInt, false); + formDataEnabledBoolean, userAgentString, fontSizeInt, displayWebpageImagesInt, nightModeInt, false); } } diff --git a/app/src/main/java/com/stoutner/privacybrowser/fragments/SettingsFragment.java b/app/src/main/java/com/stoutner/privacybrowser/fragments/SettingsFragment.java index 6ce6cdcb..cbacb385 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/fragments/SettingsFragment.java +++ b/app/src/main/java/com/stoutner/privacybrowser/fragments/SettingsFragment.java @@ -75,30 +75,34 @@ public class SettingsFragment extends PreferenceFragment { final Preference swipeToRefreshPreference = findPreference("swipe_to_refresh"); final Preference displayAdditionalAppBarIconsPreference = findPreference("display_additional_app_bar_icons"); final Preference darkThemePreference = findPreference("dark_theme"); + final Preference nightModePreference = findPreference("night_mode"); final Preference displayWebpageImagesPreference = findPreference("display_webpage_images"); // Set dependencies. - domStoragePreference.setDependency("javascript_enabled"); torHomepagePreference.setDependency("proxy_through_orbot"); torSearchPreference.setDependency("proxy_through_orbot"); hideSystemBarsPreference.setDependency("full_screen_browsing_mode"); - // Get strings from the preferences. + // Get Strings from the preferences. String torSearchString = savedPreferences.getString("tor_search", "https://3g2upl4pq6kufc4m.onion/html/?q="); String searchString = savedPreferences.getString("search", "https://duckduckgo.com/html/?q="); // Get booleans from the preferences. - boolean javaScriptEnabledBoolean = savedPreferences.getBoolean("javascript_enabled", false); + final boolean javaScriptEnabledBoolean = savedPreferences.getBoolean("javascript_enabled", false); boolean firstPartyCookiesEnabledBoolean = savedPreferences.getBoolean("first_party_cookies_enabled", false); boolean thirdPartyCookiesEnabledBoolean = savedPreferences.getBoolean("third_party_cookies_enabled", false); boolean proxyThroughOrbotBoolean = savedPreferences.getBoolean("proxy_through_orbot", false); boolean fullScreenBrowsingModeBoolean = savedPreferences.getBoolean("full_screen_browsing_mode", false); boolean hideSystemBarsBoolean = savedPreferences.getBoolean("hide_system_bars", false); boolean clearEverythingBoolean = savedPreferences.getBoolean("clear_everything", true); + final boolean nightModeBoolean = savedPreferences.getBoolean("night_mode", false); // Only enable `thirdPartyCookiesPreference` if `firstPartyCookiesEnabledBoolean` is `true` and API >= 21. thirdPartyCookiesPreference.setEnabled(firstPartyCookiesEnabledBoolean && (Build.VERSION.SDK_INT >= 21)); + // Only enable `domStoragePreference` if either `javaScriptEnabledBoolean` or `nightModeBoolean` is true. + domStoragePreference.setEnabled(javaScriptEnabledBoolean || nightModeBoolean); + // We need to inflated a `WebView` to get the default user agent. LayoutInflater inflater = getActivity().getLayoutInflater(); // `@SuppressLint("InflateParams")` removes the warning about using `null` as the `ViewGroup`, which in this case makes sense because we don't want to display `bare_webview` on the screen. `false` does not attach the view to the root. @@ -177,9 +181,11 @@ public class SettingsFragment extends PreferenceFragment { // Set the default font size as the summary text for the `Default Font Size` preference when the preference screen is loaded. The default is `100`. defaultFontSizePreference.setSummary(savedPreferences.getString("default_font_size", "100") + "%%"); + // Disable `javaScriptPreference` if `nightModeBoolean` is true. JavaScript will be enabled for all web pages. + javaScriptPreference.setEnabled(!nightModeBoolean); // Set the `javaScriptPreference` icon. - if (javaScriptEnabledBoolean) { + if (javaScriptEnabledBoolean || nightModeBoolean) { javaScriptPreference.setIcon(R.drawable.javascript_enabled); } else { javaScriptPreference.setIcon(R.drawable.privacy_mode); @@ -216,17 +222,17 @@ public class SettingsFragment extends PreferenceFragment { } // Set the `domStoragePreference` icon. - if (javaScriptEnabledBoolean) { - if (savedPreferences.getBoolean("dom_storage_enabled", false)) { + if (javaScriptEnabledBoolean || nightModeBoolean) { // The preference is enabled. + if (savedPreferences.getBoolean("dom_storage_enabled", false)) { // DOM storage is enabled. domStoragePreference.setIcon(R.drawable.dom_storage_enabled); - } else { + } else { // DOM storage is disabled. if (MainWebViewActivity.darkTheme) { domStoragePreference.setIcon(R.drawable.dom_storage_disabled_dark); } else { domStoragePreference.setIcon(R.drawable.dom_storage_disabled_light); } } - } else { + } else { // The preference is disabled. The icon should be ghosted. if (MainWebViewActivity.darkTheme) { domStoragePreference.setIcon(R.drawable.dom_storage_ghosted_dark); } else { @@ -506,6 +512,21 @@ public class SettingsFragment extends PreferenceFragment { darkThemePreference.setIcon(R.drawable.theme_light); } + // Set the `nightModePreference` icon. + if (nightModeBoolean) { + if (MainWebViewActivity.darkTheme) { + nightModePreference.setIcon(R.drawable.night_mode_enabled_dark); + } else { + nightModePreference.setIcon(R.drawable.night_mode_enabled_light); + } + } else { + if (MainWebViewActivity.darkTheme) { + nightModePreference.setIcon(R.drawable.night_mode_disabled_dark); + } else { + nightModePreference.setIcon(R.drawable.night_mode_disabled_light); + } + } + // Set the `displayWebpageImagesPreference` icon. if (savedPreferences.getBoolean("display_webpage_images", true)) { if (MainWebViewActivity.darkTheme) { @@ -531,12 +552,15 @@ public class SettingsFragment extends PreferenceFragment { switch (key) { case "javascript_enabled": - // Update the icons. - if (sharedPreferences.getBoolean("javascript_enabled", false)) { - // Update the icon for `javascript_enabled`. + // Update the icons and the DOM storage preference status. + if (sharedPreferences.getBoolean("javascript_enabled", false)) { // The JavaScript preference is enabled. + // Update the icon for the JavaScript preference. javaScriptPreference.setIcon(R.drawable.javascript_enabled); - // Update the icon for `dom_storage_enabled`. + // Update the status of the DOM storage preference. + domStoragePreference.setEnabled(true); + + // Update the icon for the DOM storage preference. if (sharedPreferences.getBoolean("dom_storage_enabled", false)) { domStoragePreference.setIcon(R.drawable.dom_storage_enabled); } else { @@ -546,11 +570,14 @@ public class SettingsFragment extends PreferenceFragment { domStoragePreference.setIcon(R.drawable.dom_storage_disabled_light); } } - } else { // `javascript_enabled` is `false`. - // Update the icon for `javascript_enabled`. + } else { // The JavaScript preference is disabled. + // Update the icon for the JavaScript preference. javaScriptPreference.setIcon(R.drawable.privacy_mode); - // Set the icon for `dom_storage_disabled` to be ghosted. + // Update the status of the DOM storage preference. + domStoragePreference.setEnabled(false); + + // Set the icon for DOM storage preference to be ghosted. if (MainWebViewActivity.darkTheme) { domStoragePreference.setIcon(R.drawable.dom_storage_ghosted_dark); } else { @@ -1188,6 +1215,60 @@ public class SettingsFragment extends PreferenceFragment { startActivity(intent); break; + case "night_mode": + // Set the URL to be reloaded on restart to apply the new night mode setting. + MainWebViewActivity.loadUrlOnRestart = true; + + // Store the current night mode status. + boolean currentNightModeBoolean = sharedPreferences.getBoolean("night_mode", false); + boolean currentJavaScriptBoolean = sharedPreferences.getBoolean("javascript_enabled", false); + + // Update the icon. + if (currentNightModeBoolean) { + if (MainWebViewActivity.darkTheme) { + nightModePreference.setIcon(R.drawable.night_mode_enabled_dark); + } else { + nightModePreference.setIcon(R.drawable.night_mode_enabled_light); + } + } else { + if (MainWebViewActivity.darkTheme) { + nightModePreference.setIcon(R.drawable.night_mode_disabled_dark); + } else { + nightModePreference.setIcon(R.drawable.night_mode_disabled_light); + } + } + + // Update the status of `javaScriptPreference` and `domStoragePreference`. + javaScriptPreference.setEnabled(!currentNightModeBoolean); + domStoragePreference.setEnabled(currentNightModeBoolean || currentJavaScriptBoolean); + + // Update the `javaScriptPreference` icon. + if (currentNightModeBoolean || currentJavaScriptBoolean) { + javaScriptPreference.setIcon(R.drawable.javascript_enabled); + } else { + javaScriptPreference.setIcon(R.drawable.privacy_mode); + } + + // Update the `domStoragePreference` icon. + if (currentNightModeBoolean || currentJavaScriptBoolean) { // The preference is enabled. + if (sharedPreferences.getBoolean("dom_storage_enabled", false)) { // DOM storage is enabled. + domStoragePreference.setIcon(R.drawable.dom_storage_enabled); + } else { // DOM storage is disabled. + if (MainWebViewActivity.darkTheme) { + domStoragePreference.setIcon(R.drawable.dom_storage_disabled_dark); + } else { + domStoragePreference.setIcon(R.drawable.dom_storage_disabled_light); + } + } + } else { // The preference is disabled. The icon should be ghosted. + if (MainWebViewActivity.darkTheme) { + domStoragePreference.setIcon(R.drawable.dom_storage_ghosted_dark); + } else { + domStoragePreference.setIcon(R.drawable.dom_storage_ghosted_light); + } + } + break; + case "display_webpage_images": if (sharedPreferences.getBoolean("display_webpage_images", true)) { // Update the icon. @@ -1198,7 +1279,7 @@ public class SettingsFragment extends PreferenceFragment { } // `mainWebView` does not need to be reloaded because unloaded images will load automatically. - MainWebViewActivity.reloadOnRestartBoolean = false; + MainWebViewActivity.reloadOnRestart = false; } else { // Update the icon. if (MainWebViewActivity.darkTheme) { @@ -1208,7 +1289,7 @@ public class SettingsFragment extends PreferenceFragment { } // Set `mainWebView` to reload on restart to remove the current images. - MainWebViewActivity.reloadOnRestartBoolean = true; + MainWebViewActivity.reloadOnRestart = true; } break; } diff --git a/app/src/main/java/com/stoutner/privacybrowser/helpers/DomainsDatabaseHelper.java b/app/src/main/java/com/stoutner/privacybrowser/helpers/DomainsDatabaseHelper.java index 3100d694..0331a01f 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/helpers/DomainsDatabaseHelper.java +++ b/app/src/main/java/com/stoutner/privacybrowser/helpers/DomainsDatabaseHelper.java @@ -26,7 +26,7 @@ import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; public class DomainsDatabaseHelper extends SQLiteOpenHelper { - private static final int SCHEMA_VERSION = 3; + private static final int SCHEMA_VERSION = 4; private static final String DOMAINS_DATABASE = "domains.db"; private static final String DOMAINS_TABLE = "domains"; @@ -40,6 +40,7 @@ public class DomainsDatabaseHelper extends SQLiteOpenHelper { public static final String USER_AGENT = "useragent"; public static final String FONT_SIZE = "fontsize"; public static final String DISPLAY_IMAGES = "displayimages"; + public static final String NIGHT_MODE = "nightmode"; public static final String PINNED_SSL_CERTIFICATE = "pinnedsslcertificate"; public static final String SSL_ISSUED_TO_COMMON_NAME = "sslissuedtocommonname"; public static final String SSL_ISSUED_TO_ORGANIZATION = "sslissuedtoorganization"; @@ -50,10 +51,16 @@ public class DomainsDatabaseHelper extends SQLiteOpenHelper { public static final String SSL_START_DATE = "sslstartdate"; public static final String SSL_END_DATE = "sslenddate"; + // Display webpage images constants. public static final int DISPLAY_WEBPAGE_IMAGES_SYSTEM_DEFAULT = 0; public static final int DISPLAY_WEBPAGE_IMAGES_ENABLED = 1; public static final int DISPLAY_WEBPAGE_IMAGES_DISABLED = 2; + // Night mode constants. + public static final int NIGHT_MODE_SYSTEM_DEFAULT = 0; + public static final int NIGHT_MODE_ENABLED = 1; + public static final int NIGHT_MODE_DISABLED = 2; + // Initialize the database. The lint warnings for the unused parameters are suppressed. public DomainsDatabaseHelper(Context context, @SuppressWarnings("UnusedParameters") String name, SQLiteDatabase.CursorFactory cursorFactory, @SuppressWarnings("UnusedParameters") int version) { super(context, DOMAINS_DATABASE, cursorFactory, SCHEMA_VERSION); @@ -73,6 +80,7 @@ public class DomainsDatabaseHelper extends SQLiteOpenHelper { USER_AGENT + " TEXT, " + FONT_SIZE + " INTEGER, " + DISPLAY_IMAGES + " INTEGER, " + + NIGHT_MODE + " INTEGER, " + PINNED_SSL_CERTIFICATE + " BOOLEAN, " + SSL_ISSUED_TO_COMMON_NAME + " TEXT, " + SSL_ISSUED_TO_ORGANIZATION + " TEXT, " + @@ -108,6 +116,11 @@ public class DomainsDatabaseHelper extends SQLiteOpenHelper { domainsDatabase.execSQL("ALTER TABLE " + DOMAINS_TABLE + " ADD COLUMN " + SSL_ISSUED_BY_ORGANIZATIONAL_UNIT + " TEXT"); domainsDatabase.execSQL("ALTER TABLE " + DOMAINS_TABLE + " ADD COLUMN " + SSL_START_DATE + " INTEGER"); domainsDatabase.execSQL("ALTER TABLE " + DOMAINS_TABLE + " ADD COLUMN " + SSL_END_DATE + " INTEGER"); + + // Upgrade from `SCHEMA_VERSION` 3. + case 3: + // Add the `NIGHT_MODE` column. + domainsDatabase.execSQL("ALTER TABLE " + DOMAINS_TABLE + " ADD COLUMN " + NIGHT_MODE + " INTEGER"); } } @@ -166,7 +179,7 @@ public class DomainsDatabaseHelper extends SQLiteOpenHelper { // Store the domain data in a `ContentValues`. ContentValues domainContentValues = new ContentValues(); - // Create entries for each field in the database. The ID is created automatically. + // Create entries for the database fields. The ID is created automatically. The pinned SSL certificate information is not created unless added by the user. domainContentValues.put(DOMAIN_NAME, domainName); domainContentValues.put(ENABLE_JAVASCRIPT, false); domainContentValues.put(ENABLE_FIRST_PARTY_COOKIES, false); @@ -176,6 +189,7 @@ public class DomainsDatabaseHelper extends SQLiteOpenHelper { domainContentValues.put(USER_AGENT, "System default user agent"); domainContentValues.put(FONT_SIZE, 0); domainContentValues.put(DISPLAY_IMAGES, 0); + domainContentValues.put(NIGHT_MODE, 0); // Get a writable database handle. SQLiteDatabase domainsDatabase = this.getWritableDatabase(); @@ -186,11 +200,12 @@ public class DomainsDatabaseHelper extends SQLiteOpenHelper { // Close the database handle. domainsDatabase.close(); + // Return the new domain database ID. return newDomainDatabaseId; } public void updateDomainExceptCertificate(int databaseId, String domainName, boolean javaScriptEnabled, boolean firstPartyCookiesEnabled, boolean thirdPartyCookiesEnabled, boolean domStorageEnabled, boolean formDataEnabled, String userAgent, int fontSize, - int displayImages, boolean pinnedSslCertificate) { + int displayImages, int nightMode, boolean pinnedSslCertificate) { // Store the domain data in a `ContentValues`. ContentValues domainContentValues = new ContentValues(); @@ -204,6 +219,7 @@ public class DomainsDatabaseHelper extends SQLiteOpenHelper { domainContentValues.put(USER_AGENT, userAgent); domainContentValues.put(FONT_SIZE, fontSize); domainContentValues.put(DISPLAY_IMAGES, displayImages); + domainContentValues.put(NIGHT_MODE, nightMode); domainContentValues.put(PINNED_SSL_CERTIFICATE, pinnedSslCertificate); // Get a writable database handle. @@ -217,7 +233,7 @@ public class DomainsDatabaseHelper extends SQLiteOpenHelper { } public void updateDomainWithCertificate(int databaseId, String domainName, boolean javaScriptEnabled, boolean firstPartyCookiesEnabled, boolean thirdPartyCookiesEnabled, boolean domStorageEnabled, boolean formDataEnabled, String userAgent, int fontSize, - int displayImages, boolean pinnedSslCertificate, String sslIssuedToCommonName, String sslIssuedToOrganization, String sslIssuedToOrganizationalUnit, String sslIssuedByCommonName, String sslIssuedByOrganization, + int displayImages, int nightMode, boolean pinnedSslCertificate, String sslIssuedToCommonName, String sslIssuedToOrganization, String sslIssuedToOrganizationalUnit, String sslIssuedByCommonName, String sslIssuedByOrganization, String sslIssuedByOrganizationalUnit, long sslStartDate, long sslEndDate) { // Store the domain data in a `ContentValues`. ContentValues domainContentValues = new ContentValues(); @@ -232,6 +248,7 @@ public class DomainsDatabaseHelper extends SQLiteOpenHelper { domainContentValues.put(USER_AGENT, userAgent); domainContentValues.put(FONT_SIZE, fontSize); domainContentValues.put(DISPLAY_IMAGES, displayImages); + domainContentValues.put(NIGHT_MODE, nightMode); domainContentValues.put(PINNED_SSL_CERTIFICATE, pinnedSslCertificate); domainContentValues.put(SSL_ISSUED_TO_COMMON_NAME, sslIssuedToCommonName); domainContentValues.put(SSL_ISSUED_TO_ORGANIZATION, sslIssuedToOrganization); diff --git a/app/src/main/res/drawable/add_dark.xml b/app/src/main/res/drawable/add_dark.xml index 1da26b74..da95078e 100644 --- a/app/src/main/res/drawable/add_dark.xml +++ b/app/src/main/res/drawable/add_dark.xml @@ -6,7 +6,7 @@ android:viewportHeight="24.0" android:viewportWidth="24.0" > - + diff --git a/app/src/main/res/drawable/add_light.xml b/app/src/main/res/drawable/add_light.xml index a64e797b..3719987b 100644 --- a/app/src/main/res/drawable/add_light.xml +++ b/app/src/main/res/drawable/add_light.xml @@ -6,7 +6,7 @@ android:viewportHeight="24.0" android:viewportWidth="24.0" > - + diff --git a/app/src/main/res/drawable/night_mode_disabled_dark.xml b/app/src/main/res/drawable/night_mode_disabled_dark.xml new file mode 100644 index 00000000..175e81bb --- /dev/null +++ b/app/src/main/res/drawable/night_mode_disabled_dark.xml @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/night_mode_disabled_light.xml b/app/src/main/res/drawable/night_mode_disabled_light.xml new file mode 100644 index 00000000..81af0370 --- /dev/null +++ b/app/src/main/res/drawable/night_mode_disabled_light.xml @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/night_mode_enabled_dark.xml b/app/src/main/res/drawable/night_mode_enabled_dark.xml new file mode 100644 index 00000000..b71a9838 --- /dev/null +++ b/app/src/main/res/drawable/night_mode_enabled_dark.xml @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/night_mode_enabled_light.xml b/app/src/main/res/drawable/night_mode_enabled_light.xml new file mode 100644 index 00000000..2d13d31b --- /dev/null +++ b/app/src/main/res/drawable/night_mode_enabled_light.xml @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/privacy_mode.xml b/app/src/main/res/drawable/privacy_mode.xml index d5879ce9..8d12208e 100644 --- a/app/src/main/res/drawable/privacy_mode.xml +++ b/app/src/main/res/drawable/privacy_mode.xml @@ -8,21 +8,21 @@ android:viewportHeight="256.0" android:viewportWidth="256.0" > - + - + - + - + diff --git a/app/src/main/res/drawable/refresh_disabled_light.xml b/app/src/main/res/drawable/refresh_disabled_light.xml index 2758345b..37eb36f6 100644 --- a/app/src/main/res/drawable/refresh_disabled_light.xml +++ b/app/src/main/res/drawable/refresh_disabled_light.xml @@ -11,7 +11,7 @@ android:viewportWidth="24.0" tools:ignore="VectorRaster" > - + diff --git a/app/src/main/res/drawable/refresh_enabled_dark.xml b/app/src/main/res/drawable/refresh_enabled_dark.xml index 6c17502f..23fd9c6b 100644 --- a/app/src/main/res/drawable/refresh_enabled_dark.xml +++ b/app/src/main/res/drawable/refresh_enabled_dark.xml @@ -11,7 +11,7 @@ android:viewportWidth="24.0" tools:ignore="VectorRaster" > - + diff --git a/app/src/main/res/drawable/refresh_enabled_light.xml b/app/src/main/res/drawable/refresh_enabled_light.xml index f2e0916b..3dd3ecab 100644 --- a/app/src/main/res/drawable/refresh_enabled_light.xml +++ b/app/src/main/res/drawable/refresh_enabled_light.xml @@ -11,7 +11,7 @@ android:viewportWidth="24.0" tools:ignore="VectorRaster" > - + diff --git a/app/src/main/res/layout/domain_settings_fragment.xml b/app/src/main/res/layout/domain_settings_fragment.xml index b27a1c41..73c8e5da 100644 --- a/app/src/main/res/layout/domain_settings_fragment.xml +++ b/app/src/main/res/layout/domain_settings_fragment.xml @@ -341,6 +341,43 @@ android:textSize="13sp" /> + + + + + + + + + + + + + . --> + diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 4111fc6a..d99b3371 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -204,7 +204,7 @@ Nombre de dominio Dominio borrado *. puede ser añadido a un dominio para incluir todos los subdominios (p.ej. *.stoutner.com) - + Por defecto del sistema Imágenes habilitadas Imágenes deshabilitadas diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 2dcf2baf..e73cf7a2 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -206,7 +206,7 @@ Nome del Dominio Dominio Eliminato è possibile anteporre *. a un dominio per includere tutti i sottodomini (es. *.stoutner.com) - + Impostazioni di default Abilita Immagini Disabilita Immagini diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c15727c5..62e75a04 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -209,11 +209,16 @@ Domain name Domain deleted *. may be prepended to a domain to include all subdomains (eg. *.stoutner.com) - + System default Images enabled Images disabled + + System default + Night mode enabled + Night mode disabled + Pinned SSL certificate Saved SSL certificate Current website SSL certificate @@ -425,6 +430,8 @@ Display icons for toggling cookies, DOM storage, and form data in the app bar if there is room. Dark theme Changing the theme will restart Privacy Browser. + Night mode + Enabling night mode will also enable JavaScript for all web pages. Display webpage images Disable to conserve bandwidth. diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index fbfb633e..160536df 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -231,6 +231,12 @@ android:summary="@string/dark_theme_summary" android:defaultValue="false" /> + +