<w>mitm</w>
<w>mozilla</w>
<w>navigationview</w>
+ <w>nightmode</w>
<w>nojs</w>
<w>oname</w>
<w>orbot</w>
<w>subfolders</w>
<w>tablayout</w>
<w>techrepublic</w>
+ <w>textarea</w>
<w>textview</w>
<w>theverge</w>
<w>torproject</w>
along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>. -->
<!-- `android:layout_weight="1"` sets the `RelativeLayout` to fill the rest of the screen because it is encapsulated in a `LinearLayout` with `android:orientation="vertical"`.-->
+<!-- `android:background=@color/gray_900` sets the background color that is displayed when a website is loading in night mode. -->
<RelativeLayout
android:id="@+id/main_webview_relativelayout"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
+ android:background="@color/gray_900"
tools:context="com.stoutner.privacybrowser.activities.MainWebViewActivity"
- tools:showIn="@layout/main_drawerlayout">
+ tools:showIn="@layout/main_drawerlayout" >
<com.google.android.gms.ads.AdView
android:id="@+id/adview"
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true"
ads:adSize="SMART_BANNER"
- ads:adUnitId="@string/ad_id">
+ ads:adUnitId="@string/ad_id" >
</com.google.android.gms.ads.AdView>
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipe_refreshlayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_above="@id/adview">
+ android:layout_above="@id/adview" >
<!-- Google does not currently want to support hiding the AppBar on scroll for a WebView child with the Support Toolbar. https://code.google.com/p/android/issues/detail?id=200394 -->
<WebView
<p><img class="left" src="../en/images/clear_and_exit.png">
is derived from ic_exit_to_app, which is part of the <a href="https://material.io/icons/">Android Material icon set</a> and is released under the <a href ="https://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>.
The full text of the license is below. Modifications copyright © 2017 <a href="mailto:soren@stoutner.com">Soren Stoutner</a>. The resulting image is released under the <a href="https://www.gnu.org/licenses/gpl-3.0.html">GPLv3+ license</a>.</p>
+ <p><img class="left" src="../en/images/night_mode.png">
+ is derived from ic_compare, which is part of the <a href="https://material.io/icons/">Android Material icon set</a> and is released under the <a href ="https://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>.
+ The full text of the license is below. Modifications copyright © 2017 <a href="mailto:soren@stoutner.com">Soren Stoutner</a>. The resulting image is released under the <a href="https://www.gnu.org/licenses/gpl-3.0.html">GPLv3+ license</a>.</p>
<p><img class="left" src="../en/images/orbot.png"> orbot is a modified version of <a href="https://gitweb.torproject.org/orbot.git/tree/app/src/main/res/drawable-xxxhdpi/ic_stat_tor.png">the status icon from the Orbot project</a>, which is copyright
2009-2010 Nathan Freitas, The Guardian Project. It is released under the <a href="https://gitweb.torproject.org/orbot.git/tree/LICENSE">3-clause BSD license</a>.
The full text of the license is below. Modifications copyright © 2017 <a href="mailto:soren@stoutner.com">Soren Stoutner</a>. The resulting image is released under the <a href="https://www.gnu.org/licenses/gpl-3.0.html">GPLv3+ license</a>.</p>
<p><img class="left" src="images/clear_and_exit.png">
is derived from ic_exit_to_app, which is part of the <a href="https://material.io/icons/">Android Material icon set</a> and is released under the <a href ="https://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>.
The full text of the license is below. Modifications copyright © 2017 <a href="mailto:soren@stoutner.com">Soren Stoutner</a>. The resulting image is released under the <a href="https://www.gnu.org/licenses/gpl-3.0.html">GPLv3+ license</a>.</p>
+ <p><img class="left" src="images/night_mode.png">
+ is derived from ic_compare, which is part of the <a href="https://material.io/icons/">Android Material icon set</a> and is released under the <a href ="https://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>.
+ The full text of the license is below. Modifications copyright © 2017 <a href="mailto:soren@stoutner.com">Soren Stoutner</a>. The resulting image is released under the <a href="https://www.gnu.org/licenses/gpl-3.0.html">GPLv3+ license</a>.</p>
<p><img class="left" src="images/orbot.png"> orbot is a modified version of <a href="https://gitweb.torproject.org/orbot.git/tree/app/src/main/res/drawable-xxxhdpi/ic_stat_tor.png">the status icon from the Orbot project</a>, which is copyright
2009-2010 Nathan Freitas, The Guardian Project. It is released under the <a href="https://gitweb.torproject.org/orbot.git/tree/LICENSE">3-clause BSD license</a>.
The full text of the license is below. Modifications copyright © 2017 <a href="mailto:soren@stoutner.com">Soren Stoutner</a>. The resulting image is released under the <a href="https://www.gnu.org/licenses/gpl-3.0.html">GPLv3+ license</a>.</p>
<a href="https://www.gnu.org/licenses/gpl-3.0.html">licencia GPLv3+</a>.</p>
<p><img class="left" src="../en/images/clear_and_exit.png">
deriva de ic_exit_to_app, que es parte del <a href="https://material.io/icons/">conjunto de iconos Android Material</a> y es liberado bajo la <a href ="https://www.apache.org/licenses/LICENSE-2.0">Licencia Apache 2.0</a>.
- El texto completo de la licencia se encuentra debajo. Copyright de modificaciones © 2017 <a href="mailto:soren@stoutner.com">Soren Stoutner</a>. La imagen resultante se libera bajo la
- <a href="https://www.gnu.org/licenses/gpl-3.0.html">licencia GPLv3+</a>.</p>
+ El texto completo de la licencia se encuentra debajo. Copyright de modificaciones © 2017 <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.
+ La imagen resultante se libera bajo la <a href="https://www.gnu.org/licenses/gpl-3.0.html">licencia GPLv3+</a>.</p>
+ <p><img class="left" src="../en/images/night_mode.png">
+ deriva de ic_exit_to_app, que es parte del <a href="https://material.io/icons/">conjunto de iconos Android Material</a> y es liberado bajo la <a href ="https://www.apache.org/licenses/LICENSE-2.0">Licencia Apache 2.0</a>.
+ El texto completo de la licencia se encuentra debajo. Copyright de modificaciones © 2017 <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.
+ La imagen resultante se libera bajo la <a href="https://www.gnu.org/licenses/gpl-3.0.html">licencia GPLv3+</a>.</p>
<p><img class="left" src="../en/images/orbot.png"> orbot es una versión modificada del <a href="https://gitweb.torproject.org/orbot.git/tree/app/src/main/res/drawable-xxxhdpi/ic_stat_tor.png">icono de estado del proyecto Orbot</a>,
que tiene copyright 2009-2010 por Nathan Freitas, The Guardian Project. Es liberado bajo la <a href="https://gitweb.torproject.org/orbot.git/tree/LICENSE">licencia BSD modificada (de 3 cláusulas)</a>.
El texto completo de la licencia se encuentra debajo. Copyright de modificaciones © 2017 <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.
è stata derivata da ic_exit_to_app, che fa parte dell'<a href="https://material.io/icons/">Android Material icon set</a> ed è stata rilasciata sotto <a href="https://www.apache.org/licenses/LICENSE-2.0">Licenza Apache 2.0</a>.
Il testo completo della licenza è riportato di seguito. Copyright delle modifiche © 2017 <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.
L'immagine risultante è rilasciata sotto <a href="https://www.gnu.org/licenses/gpl-3.0.html">Licenza GPLv3+</a>.</p>
+ <p><img class="left" src="../en/images/night_mode.png">
+ è stata derivata da ic_compare, che fa parte dell'<a href="https://material.io/icons/">Android Material icon set</a> ed è stata rilasciata sotto <a href="https://www.apache.org/licenses/LICENSE-2.0">Licenza Apache 2.0</a>.
+ Il testo completo della licenza è riportato di seguito. Copyright delle modifiche © 2017 <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.
+ L'immagine risultante è rilasciata sotto <a href="https://www.gnu.org/licenses/gpl-3.0.html">Licenza GPLv3+</a>.</p>
<p><img class="left" src="../en/images/orbot.png"> orbot è una versione modificata della <a href="https://gitweb.torproject.org/orbot.git/tree/app/src/main/res/drawable-xxxhdpi/ic_stat_tor.png">icona di stato del progetto Orbot</a>, il cui copyright
è 2009-2010 Nathan Freitas, The Guardian Project. E' rilasciata sotto <a href="https://gitweb.torproject.org/orbot.git/tree/LICENSE">3-clause BSD license</a>. Il testo completo della Licenza è riportato di seguito.
Copyright delle modifiche © 2017<a href="mailto:soren@stoutner.com">Soren Stoutner</a>. L'immagine risultante è rilasciata sotto <a href="https://www.gnu.org/licenses/gpl-3.0.html">Licenza GPLv3+</a>.</p>
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);
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.
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;
// 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);
}
}
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;
// `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;
// `customHeader` is used in `onCreate()`, `onOptionsItemSelected()`, `onCreateContextMenu()`, and `loadUrl()`.
private final Map<String, String> 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;
// 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.
}
}
- // 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.
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);
}
thirdPartyCookiesEnabled = false;
domStorageEnabled = false;
saveFormDataEnabled = false;
+ nightMode = false;
// Initialize `webViewTitle`.
webViewTitle = getString(R.string.no_title);
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;
}
}
// 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.
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));
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;
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);
// 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);
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);
// 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));
final ArrayAdapter<CharSequence> userAgentEntryValuesArrayAdapter = ArrayAdapter.createFromResource(context, R.array.domain_settings_user_agent_entry_values, R.layout.spinner_item);
ArrayAdapter<CharSequence> fontSizeArrayAdapter = ArrayAdapter.createFromResource(context, R.array.domain_settings_font_size_entries, R.layout.spinner_item);
ArrayAdapter<CharSequence> fontSizeEntryValuesArrayAdapter = ArrayAdapter.createFromResource(context, R.array.domain_settings_font_size_entry_values, R.layout.spinner_item);
- final ArrayAdapter<CharSequence> displayImagesArrayAdapter = ArrayAdapter.createFromResource(context, R.array.display_website_images_array, R.layout.spinner_item);
+ final ArrayAdapter<CharSequence> displayImagesArrayAdapter = ArrayAdapter.createFromResource(context, R.array.display_webpage_images_array, R.layout.spinner_item);
+ ArrayAdapter<CharSequence> 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);
}
});
- // 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.
}
// 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);
}
}
} 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);
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));
}
}
+ // 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);
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));
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);
// 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));
// 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() {
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);
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.
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;
// 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);
}
}
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.
// 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);
}
// 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 {
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) {
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 {
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 {
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.
}
// `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) {
}
// Set `mainWebView` to reload on restart to remove the current images.
- MainWebViewActivity.reloadOnRestartBoolean = true;
+ MainWebViewActivity.reloadOnRestart = true;
}
break;
}
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";
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";
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);
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, " +
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");
}
}
// 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);
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();
// 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();
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.
}
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();
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);
android:viewportHeight="24.0"
android:viewportWidth="24.0" >
- <!-- We have to use a hard coded color until API >= 21. Then we can use `@color`. -->
+ <!-- A hard coded color must be used until API >= 21. Then `@color` may be used. -->
<path
android:fillColor="#FFE0E0E0"
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
android:viewportHeight="24.0"
android:viewportWidth="24.0" >
- <!-- We have to use a hard coded color until API >= 21. Then we can use `@color`. -->
+ <!-- A hard coded color must be used until API >= 21. Then `@color` may be used. -->
<path
android:fillColor="#FFFFFFFF"
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
--- /dev/null
+<!-- `night_mode_disabled_dark.xml` is derived from `ic_compare`, which is part of the Android Material icon set. `ic_compare` is released under the Apache License 2.0.
+ Modifications copyright © 2017 Soren Stoutner <soren@stoutner.com>. The resulting image is released under the GPLv3+ license. -->
+
+<!-- `tools:ignore="VectorRaster"` removes the lint warning about `android:autoMirrored="true"` not applying to API < 21. -->
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:height="24dp"
+ android:width="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0"
+ android:autoMirrored="true"
+ tools:ignore="VectorRaster" >
+
+ <!-- A hard coded color must be used until API >= 21. Then `@color` may be used. -->
+ <path
+ android:fillColor="#FF9E9E9E"
+ android:pathData="m14,3h5c1.1,0 2,0.9 2,2v14c0,1.1 -0.9,2 -2,2h-5v2L12,23L12,1h2zM14,18h5L14,12ZM5,3h5L10,5L5,5v13l5,-6v9L5,21C3.9,21 3,20.1 3,19L3,5C3,3.9 3.9,3 5,3Z"/>
+</vector>
--- /dev/null
+<!-- `night_mode_disabled_dark.xml` is derived from `ic_compare`, which is part of the Android Material icon set. `ic_compare` is released under the Apache License 2.0.
+ Modifications copyright © 2017 Soren Stoutner <soren@stoutner.com>. The resulting image is released under the GPLv3+ license. -->
+
+<!-- `tools:ignore="VectorRaster"` removes the lint warning about `android:autoMirrored="true"` not applying to API < 21. -->
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:height="24dp"
+ android:width="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0"
+ android:autoMirrored="true"
+ tools:ignore="VectorRaster" >
+
+ <!-- A hard coded color must be used until API >= 21. Then `@color` may be used. -->
+ <path
+ android:fillColor="#88000000"
+ android:pathData="m14,3h5c1.1,0 2,0.9 2,2v14c0,1.1 -0.9,2 -2,2h-5v2L12,23L12,1h2zM14,18h5L14,12ZM5,3h5L10,5L5,5v13l5,-6v9L5,21C3.9,21 3,20.1 3,19L3,5C3,3.9 3.9,3 5,3Z"/>
+</vector>
--- /dev/null
+<!-- `night_mode_enabled_dark.xml` is derived from `ic_compare`, which is part of the Android Material icon set. `ic_compare` is released under the Apache License 2.0.
+ Modifications copyright © 2017 Soren Stoutner <soren@stoutner.com>. The resulting image is released under the GPLv3+ license. -->
+
+<!-- `tools:ignore="VectorRaster"` removes the lint warning about `android:autoMirrored="true"` not applying to API < 21. -->
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:height="24dp"
+ android:width="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0"
+ android:autoMirrored="true"
+ tools:ignore="VectorRaster" >
+
+ <!-- A hard coded color must be used until API >= 21. Then `@color` may be used. -->
+ <path
+ android:fillColor="#FF1E88E5"
+ android:pathData="m14,3h5c1.1,0 2,0.9 2,2v14c0,1.1 -0.9,2 -2,2h-5v2L12,23L12,1h2zM14,18h5L14,12ZM5,3h5L10,5L5,5v13l5,-6v9L5,21C3.9,21 3,20.1 3,19L3,5C3,3.9 3.9,3 5,3Z"/>
+</vector>
--- /dev/null
+<!-- `night_mode_enabled_light.xml` is derived from `ic_compare`, which is part of the Android Material icon set. `ic_compare` is released under the Apache License 2.0.
+ Modifications copyright © 2017 Soren Stoutner <soren@stoutner.com>. The resulting image is released under the GPLv3+ license. -->
+
+<!-- `tools:ignore="VectorRaster"` removes the lint warning about `android:autoMirrored="true"` not applying to API < 21. -->
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:height="24dp"
+ android:width="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0"
+ android:autoMirrored="true"
+ tools:ignore="VectorRaster" >
+
+ <!-- A hard coded color must be used until API >= 21. Then `@color` may be used. -->
+ <path
+ android:fillColor="#FF1565C0"
+ android:pathData="m14,3h5c1.1,0 2,0.9 2,2v14c0,1.1 -0.9,2 -2,2h-5v2L12,23L12,1h2zM14,18h5L14,12ZM5,3h5L10,5L5,5v13l5,-6v9L5,21C3.9,21 3,20.1 3,19L3,5C3,3.9 3.9,3 5,3Z"/>
+</vector>
android:viewportHeight="256.0"
android:viewportWidth="256.0" >
- <!-- We have to use a hard coded color until API >= 21. Then we can use `@color`. -->
+ <!-- A hard coded color must be used until API >= 21. Then `@color` may be used. -->
<path
android:fillAlpha="1"
android:fillColor="#0d4781"
android:pathData="m128,12.8 l-94.25,41.89 0,62.84c0,58.12 40.22,112.48 94.25,125.67 54.04,-13.2 94.25,-67.55 94.25,-125.67l0,-62.84z"
android:strokeColor="#00000000" />
- <!-- We have to use a hard coded color until API >= 21. Then we can use `@color`. -->
+ <!-- A hard coded color must be used until API >= 21. Then `@color` may be used. -->
<path
android:fillAlpha="1"
android:fillColor="#1976d2"
android:pathData="m128,0 l-104.73,46.55 0,69.82C23.27,180.95 67.96,241.34 128,256 188.04,241.34 232.73,180.95 232.73,116.36l0,-69.82L128,0ZM128,127.88 L209.45,127.88C203.29,175.83 171.29,218.53 128,231.91l0,-103.91 -81.45,0 0,-66.33L128,25.48l0,102.4z"
android:strokeColor="#00000000" />
- <!-- We have to use a hard coded color until API >= 21. Then we can use `@color`. -->
+ <!-- A hard coded color must be used until API >= 21. Then `@color` may be used. -->
<path
android:fillAlpha="1"
android:fillColor="#ffffff"
android:viewportWidth="24.0"
tools:ignore="VectorRaster" >
- <!-- We have to use a hard coded color until API >= 21. Then we can use `@color`. -->
+ <!-- A hard coded color must be used until API >= 21. Then `@color` may be used. -->
<path
android:fillColor="#FF9E9E9E"
android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z"/>
android:viewportWidth="24.0"
tools:ignore="VectorRaster" >
- <!-- We have to use a hard coded color until API >= 21. Then we can use `@color`. -->
+ <!-- A hard coded color must be used until API >= 21. Then `@color` may be used. -->
<path
android:fillColor="#88000000"
android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z"/>
android:viewportWidth="24.0"
tools:ignore="VectorRaster" >
- <!-- We have to use a hard coded color until API >= 21. Then we can use `@color`. -->
+ <!-- A hard coded color must be used until API >= 21. Then `@color` may be used. -->
<path
android:fillColor="#FF1E88E5"
android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z"/>
android:viewportWidth="24.0"
tools:ignore="VectorRaster" >
- <!-- We have to use a hard coded color until API >= 21. Then we can use `@color`. -->
+ <!-- A hard coded color must be used until API >= 21. Then `@color` may be used. -->
<path
android:fillColor="#FF1565C0"
android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z"/>
android:textSize="13sp" />
</LinearLayout>
+ <!-- Night Mode. -->
+ <LinearLayout
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:orientation="vertical"
+ android:layout_marginTop="14dp"
+ android:layout_marginBottom="14dp" >
+
+ <LinearLayout
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:orientation="horizontal" >
+
+ <ImageView
+ android:id="@+id/domain_settings_night_mode_imageview"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_marginTop="1dp"
+ android:layout_marginEnd="10dp"
+ android:layout_gravity="center_vertical"
+ android:contentDescription="@string/night_mode" />
+
+ <Spinner
+ android:id="@+id/domain_settings_night_mode_spinner"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent" />
+ </LinearLayout>
+
+ <TextView
+ android:id="@+id/domain_settings_night_mode_textview"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:layout_marginStart="45dp"
+ android:layout_marginEnd="36dp"
+ android:textSize="13sp" />
+ </LinearLayout>
+
<!-- Pinned SSL Certificate -->
<LinearLayout
android:layout_height="wrap_content"
along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>. -->
<!-- `android:layout_weight="1"` sets the `RelativeLayout` to fill the rest of the screen because it is encapsulated in a `LinearLayout` with `android:orientation="vertical"`. -->
+<!-- `android:background=@color/gray_900` sets the background color that is displayed when a website is loading in night mode. -->
<RelativeLayout
android:id="@+id/main_webview_relativelayout"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="0dp"
android:layout_width="match_parent"
android:layout_weight="1"
+ android:background="@color/gray_900"
tools:context="com.stoutner.privacybrowser.activities.MainWebViewActivity" >
<!-- This `TextView` has an id of `adView` so that the ad commands (which do nothing in the standard flavor) don't produce errors. -->
<string name="domain_name">Nombre de dominio</string>
<string name="domain_deleted">Dominio borrado</string>
<string name="domain_name_instructions">*. puede ser añadido a un dominio para incluir todos los subdominios (p.ej. *.stoutner.com)</string>
- <string-array name="display_website_images_array">
+ <string-array name="display_webpage_images_array">
<item>Por defecto del sistema</item>
<item>Imágenes habilitadas</item>
<item>Imágenes deshabilitadas</item>
<string name="domain_name">Nome del Dominio</string>
<string name="domain_deleted">Dominio Eliminato</string>
<string name="domain_name_instructions">è possibile anteporre *. a un dominio per includere tutti i sottodomini (es. *.stoutner.com)</string>
- <string-array name="display_website_images_array">
+ <string-array name="display_webpage_images_array">
<item>Impostazioni di default</item>
<item>Abilita Immagini</item>
<item>Disabilita Immagini</item>
<string name="domain_name">Domain name</string>
<string name="domain_deleted">Domain deleted</string>
<string name="domain_name_instructions">*. may be prepended to a domain to include all subdomains (eg. *.stoutner.com)</string>
- <string-array name="display_website_images_array">
+ <string-array name="display_webpage_images_array">
<item>System default</item>
<item>Images enabled</item>
<item>Images disabled</item>
</string-array>
+ <string-array name="night_mode_array">
+ <item>System default</item>
+ <item>Night mode enabled</item>
+ <item>Night mode disabled</item>
+ </string-array>
<string name="pinned_ssl_certificate">Pinned SSL certificate</string>
<string name="saved_ssl_certificate">Saved SSL certificate</string>
<string name="current_website_ssl_certificate">Current website SSL certificate</string>
<string name="display_additional_app_bar_icons_summary">Display icons for toggling cookies, DOM storage, and form data in the app bar if there is room.</string>
<string name="dark_theme">Dark theme</string>
<string name="dark_theme_summary">Changing the theme will restart Privacy Browser.</string>
+ <string name="night_mode">Night mode</string>
+ <string name="night_mode_summary">Enabling night mode will also enable JavaScript for all web pages.</string>
<string name="display_webpage_images">Display webpage images</string>
<string name="display_webpage_images_summary">Disable to conserve bandwidth.</string>
android:summary="@string/dark_theme_summary"
android:defaultValue="false" />
+ <SwitchPreference
+ android:key="night_mode"
+ android:title="@string/night_mode"
+ android:summary="@string/night_mode_summary"
+ android:defaultValue="false" />
+
<SwitchPreference
android:key="display_webpage_images"
android:title="@string/display_webpage_images"