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/?a=commitdiff_plain;h=8ca39b63e2d15fbb6828e255be4e0b5493c744ce;p=PrivacyBrowserAndroid.git
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" />
+
+