<w>amoled</w>
<w>androidversion</w>
<w>anonymized</w>
+ <w>apng</w>
<w>appbarlayout</w>
<w>aren</w>
<w>autoselected</w>
-Privacy Browser copyright © 2015-2017 Soren Stoutner <soren@stoutner.com>.
+Privacy Browser copyright © 2015-2018 Soren Stoutner <soren@stoutner.com>.
This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
/*
- * Copyright © 2016-2017 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2016-2018 Soren Stoutner <soren@stoutner.com>.
*
* This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
*
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:design:26.1.0'
// Only compile `com.google.firebase:firebase-ads` for the free flavor.
- freeImplementation 'com.google.firebase:firebase-ads:11.6.2'
+ freeImplementation 'com.google.firebase:firebase-ads:11.8.0'
}
// Google's documentation says the following line is required for `firebase-ads` but things work correctly without it. I have no interest in applying the Google Mobile Services plugin in the standard flavor if I don't have to.
android:screenOrientation="fullUser"
android:persistableMode="persistNever"
tools:ignore="UnusedAttribute" />
+
+ <!-- `android:configChanges="orientation|screenSize"` makes the activity not reload when the orientation changes.
+ `android:persistableMode="persistNever"` removes Privacy Browser from the recents screen on a device reboot.
+ `tools:ignore="unusedAttribute"` removes the lint warning that `persistableMode` does not apply to API < 21. -->
+ <activity
+ android:name=".activities.ViewSourceActivity"
+ android:label="@string/view_source"
+ android:parentActivityName=".activities.MainWebViewActivity"
+ android:configChanges="orientation|screenSize"
+ android:screenOrientation="fullUser"
+ android:persistableMode="persistNever"
+ tools:ignore="UnusedAttribute" />
</application>
</manifest>
<!--
- Copyright © 2016-2017 Soren Stoutner <soren@stoutner.com>.
+ Copyright © 2016-2018 Soren Stoutner <soren@stoutner.com>.
Translation 2016 Aaron Gerlach <aaron@gerlach.com>. Copyright assigned to Soren Stoutner <soren@stoutner.com>.
<body>
<h3>Copyright</h3>
- <p>Privacy Browser ist copyright © 2015-2017 von <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.</p>
+ <p>Privacy Browser ist copyright © 2015-2018 von <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.</p>
<h3>Lizenz</h3>
<p>Privacy Browser ist veröffentlicht unter der <a href="https://www.gnu.org/licenses/gpl-3.0.html">GPLv3+ Lizenz</a>. The full text of the license is below.
<h3>Attribute</h3>
<p>The list of ad servers used by the ad blocker comes from <a href="https://pgl.yoyo.org/adservers/">pgl.yoyo.org</a>.
Because a list of domain names is a list of facts, it <a href="https://www.copyright.gov/help/faq/faq-protect.html">cannot be copyrighted</a>.</p>
+ <p>Privacy Browser is built with the <a href="https://developer.android.com/topic/libraries/support-library/index.html">Android Support Library</a>,
+ which is released under the <a href="https://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>.</p>
<p><img class="left" src="../en/images/privacy_browser.png"> <img class="left" src="../en/images/privacy_browser_free.png"> <img class="left" src="../en/images/warning.png"> <img class="left" src="../en/images/javascript_enabled.png">
are derived from ic_security and ic_language, which are part of the <a href="https://material.io/icons/">Android Material icon set</a> and are 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 © 2016 <a href="mailto:soren@stoutner.com">Soren Stoutner</a>. The resulting images are released under the <a href="https://www.gnu.org/licenses/gpl-3.0.html">GPLv3+ license</a>.</p>
<!--
- Copyright © 2016-2017 Soren Stoutner <soren@stoutner.com>.
+ Copyright © 2016-2018 Soren Stoutner <soren@stoutner.com>.
Translation 2016 Aaron Gerlach <aaron@gerlach.com>. Copyright assigned to Soren Stoutner <soren@stoutner.com>.
<body>
<h3>Copyright</h3>
- <p>Privacy Browser ist copyright © 2015-2017 von <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.</p>
+ <p>Privacy Browser ist copyright © 2015-2018 von <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.</p>
<h3>Lizenz</h3>
<p>Privacy Browser ist veröffentlicht unter der <a href="https://www.gnu.org/licenses/gpl-3.0.html">GPLv3+ Lizenz</a>. The full text of the license is below.
<h3>Attribute</h3>
<p>The list of ad servers used by the ad blocker comes from <a href="https://pgl.yoyo.org/adservers/">pgl.yoyo.org</a>.
Because a list of domain names is a list of facts, it <a href="https://www.copyright.gov/help/faq/faq-protect.html">cannot be copyrighted</a>.</p>
+ <p>Privacy Browser is built with the <a href="https://developer.android.com/topic/libraries/support-library/index.html">Android Support Library</a>,
+ which is released under the <a href="https://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>.</p>
<p><img class="left" src="../en/images/privacy_browser.png"> <img class="left" src="../en/images/privacy_browser_free.png"> <img class="left" src="../en/images/warning.png"> <img class="left" src="../en/images/javascript_enabled.png">
are derived from ic_security and ic_language, which are part of the <a href="https://material.io/icons/">Android Material icon set</a> and are 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 © 2016 <a href="mailto:soren@stoutner.com">Soren Stoutner</a>. The resulting images are released under the <a href="https://www.gnu.org/licenses/gpl-3.0.html">GPLv3+ license</a>.</p>
<!--
- Copyright © 2016-2017 Soren Stoutner <soren@stoutner.com>.
+ Copyright © 2016-2018 Soren Stoutner <soren@stoutner.com>.
This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
<body>
<h3>Copyright</h3>
- <p>Privacy Browser copyright © 2015-2017 <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.</p>
+ <p>Privacy Browser copyright © 2015-2018 <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.</p>
<h3>License</h3>
<p>Privacy Browser is released under the <a href="https://www.gnu.org/licenses/gpl-3.0.html">GPLv3+ license</a>. The full text of the license is below.
<h3>Attributions</h3>
<p>The list of ad servers used by the ad blocker comes from <a href="https://pgl.yoyo.org/adservers/">pgl.yoyo.org</a>.
Because a list of domain names is a list of facts, it <a href="https://www.copyright.gov/help/faq/faq-protect.html">cannot be copyrighted</a>.</p>
+ <p>Privacy Browser is built with the <a href="https://developer.android.com/topic/libraries/support-library/index.html">Android Support Library</a>,
+ which is released under the <a href="https://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>.</p>
<p><img class="left" src="images/privacy_browser.png"> <img class="left" src="images/privacy_browser_free.png"> <img class="left" src="images/warning.png"> <img class="left" src="images/javascript_enabled.png">
are derived from ic_security and ic_language, which are part of the <a href="https://material.io/icons/">Android Material icon set</a> and are 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 © 2016 <a href="mailto:soren@stoutner.com">Soren Stoutner</a>. The resulting images are released under the <a href="https://www.gnu.org/licenses/gpl-3.0.html">GPLv3+ license</a>.</p>
<!--
- Copyright © 2016-2017 Soren Stoutner <soren@stoutner.com>.
+ Copyright © 2016-2018 Soren Stoutner <soren@stoutner.com>.
This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
<body>
<h3>Copyright</h3>
- <p>Privacy Browser copyright © 2015-2017 <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.</p>
+ <p>Privacy Browser copyright © 2015-2018 <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.</p>
<h3>License</h3>
<p>Privacy Browser is released under the <a href="https://www.gnu.org/licenses/gpl-3.0.html">GPLv3+ license</a>. The full text of the license is below.
<h3>Attributions</h3>
<p>The list of ad servers used by the ad blocker comes from <a href="https://pgl.yoyo.org/adservers/">pgl.yoyo.org</a>.
Because a list of domain names is a list of facts, it <a href="https://www.copyright.gov/help/faq/faq-protect.html">cannot be copyrighted</a>.</p>
+ <p>Privacy Browser is built with the <a href="https://developer.android.com/topic/libraries/support-library/index.html">Android Support Library</a>,
+ which is released under the <a href="https://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>.</p>
<p><img class="left" src="images/privacy_browser.png"> <img class="left" src="images/privacy_browser_free.png"> <img class="left" src="images/warning.png"> <img class="left" src="images/javascript_enabled.png">
are derived from ic_security and ic_language, which are part of the <a href="https://material.io/icons/">Android Material icon set</a> and are 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 © 2016 <a href="mailto:soren@stoutner.com">Soren Stoutner</a>. The resulting images are released under the <a href="https://www.gnu.org/licenses/gpl-3.0.html">GPLv3+ license</a>.</p>
<!--
- Copyright © 2016-2017 Soren Stoutner <soren@stoutner.com>.
+ Copyright © 2016-2018 Soren Stoutner <soren@stoutner.com>.
- Translation 2017 Jose A. León Becerra <emails@joseleon.me>. Copyright assigned to Soren Stoutner <soren@stoutner.com>.
+ Translation 2017-2018 Jose A. León Becerra <emails@joseleon.me>. Copyright assigned to Soren Stoutner <soren@stoutner.com>.
This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
<body>
<h3>Derechos de autor</h3>
- <p>Navegador Privado tiene derechos de autor © 2015-2017 por <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.</p>
+ <p>Navegador Privado tiene derechos de autor © 2015-2018 por <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.</p>
<h3>Licencia</h3>
<p>Navegador Privado está liberado bajo la licencia <a href="https://www.gnu.org/licenses/gpl-3.0.html">GPLv3+</a>. El texto completo de la licencia se encuentra en la parte inferior de este documento (se deja en el idioma original).
<h3>Atribuciones</h3>
<p>La lista de servidores publicitarios usados por el bloqueador de anuncios procede de <a href="https://pgl.yoyo.org/adservers/">pgl.yoyo.org</a>.
Ya que una lista de nombres de dominio es una lista de hechos, no puede tener <a href="https://www.copyright.gov/help/faq/faq-protect.html">derechos de autor</a>.</p>
+ <p>Navegador privado está construido con la <a href="https://developer.android.com/topic/libraries/support-library/index.html">librería de soporte de android</a>,
+ que se libera bajo la <a href="https://www.apache.org/licenses/LICENSE-2.0">Licencia Apache 2.0</a>.</p>
<p><img class="left" src="../en/images/privacy_browser.png"> <img class="left" src="../en/images/privacy_browser_free.png"> <img class="left" src="../en/images/warning.png"> <img class="left" src="../en/images/javascript_enabled.png">
derivan de ic_security y de ic_language, que son parte del <a href="https://material.io/icons/">conjunto de iconos Android Material</a> y son liberados 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 © 2016 <a href="mailto:soren@stoutner.com">Soren Stoutner</a>. Las imágenes resultantes se liberan bajo la
<!--
- Copyright © 2016-2017 Soren Stoutner <soren@stoutner.com>.
+ Copyright © 2016-2018 Soren Stoutner <soren@stoutner.com>.
- Translation 2017 Jose A. León Becerra <emails@joseleon.me>. Copyright assigned to Soren Stoutner <soren@stoutner.com>.
+ Translation 2017-2018 Jose A. León Becerra <emails@joseleon.me>. Copyright assigned to Soren Stoutner <soren@stoutner.com>.
This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
<body>
<h3>Derechos de autor</h3>
- <p>Navegador Privado tiene derechos de autor © 2015-2017 por <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.</p>
+ <p>Navegador Privado tiene derechos de autor © 2015-2018 por <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.</p>
<h3>Licencia</h3>
<p>Navegador Privado está liberado bajo la licencia <a href="https://www.gnu.org/licenses/gpl-3.0.html">GPLv3+</a>. El texto completo de la licencia se encuentra en la parte inferior de este documento (se deja en el idioma original).
<h3>Atribuciones</h3>
<p>La lista de servidores publicitarios usados por el bloqueador de anuncios procede de <a href="https://pgl.yoyo.org/adservers/">pgl.yoyo.org</a>.
Ya que una lista de nombres de dominio es una lista de hechos, no puede tener <a href="https://www.copyright.gov/help/faq/faq-protect.html">derechos de autor</a>.</p>
+ <p>Navegador privado está construido con la <a href="https://developer.android.com/topic/libraries/support-library/index.html">librería de soporte de android</a>,
+ que se libera bajo la <a href="https://www.apache.org/licenses/LICENSE-2.0">Licencia Apache 2.0</a>.</p>
<p><img class="left" src="../en/images/privacy_browser.png"> <img class="left" src="../en/images/privacy_browser_free.png"> <img class="left" src="../en/images/warning.png"> <img class="left" src="../en/images/javascript_enabled.png">
derivan de ic_security y de ic_language, que son parte del <a href="https://material.io/icons/">conjunto de iconos Android Material</a> y son liberados 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 © 2016 <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.
<!--
- Copyright © 2017 Soren Stoutner <soren@stoutner.com>.
+ Copyright © 2017-2018 Soren Stoutner <soren@stoutner.com>.
- Translation 2017 Francesco Buratti. Copyright assigned to Soren Stoutner <soren@stoutner.com>.
+ Translation 2017-2018 Francesco Buratti. Copyright assigned to Soren Stoutner <soren@stoutner.com>.
Privacy Browser is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
<body>
<h3>Copyright</h3>
- <p>Privacy Browser copyright © 2015-2017: <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.</p>
+ <p>Privacy Browser copyright © 2015-2018: <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.</p>
<h3>Licenza</h3>
<p>Privacy Browser è rilasciato con <a href="https://www.gnu.org/licenses/gpl-3.0.html">Licenza GPLv3+ </a>.
<h3>Attribuzioni</h3>
<p>La lista dei server utilizzata dalla funzionalità di blocco degli annunci è tratta da <a href="https://pgl.yoyo.org/adservers/">pgl.yoyo.org</a>.
Dal momento che si tratta di una lista di domini <a href="https://www.copyright.gov/help/faq/faq-protect.html">non può essere coperta da Copyright</a>.</p>
+ <p>Privacy Browser è sviluppato con la <a href="https://developer.android.com/topic/libraries/support-library/index.html">Android Support Library</a>,
+ che è rilasciata con <a href="https://www.apache.org/licenses/LICENSE-2.0">Licenza Apache 2.0</a>.</p>
<p><img class="left" src="../en/images/privacy_browser.png"> <img class="left" src="../en/images/privacy_browser_free.png"> <img class="left" src="../en/images/warning.png"> <img class="left" src="../en/images/javascript_enabled.png">
sono state derivate da ic_security e ic_language, che fanno parte dell'<a href="https://material.io/icons/">Android Material icon set</a> e sono state rilasciate 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 © 2016 <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.
<!--
- Copyright © 2017 Soren Stoutner <soren@stoutner.com>.
+ Copyright © 2017-2018 Soren Stoutner <soren@stoutner.com>.
- Translation 2017 Francesco Buratti. Copyright assigned to Soren Stoutner <soren@stoutner.com>.
+ Translation 2017-2018 Francesco Buratti. Copyright assigned to Soren Stoutner <soren@stoutner.com>.
Privacy Browser is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
<body>
<h3>Copyright</h3>
- <p>Privacy Browser copyright © 2015-2017: <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.</p>
+ <p>Privacy Browser copyright © 2015-2018: <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.</p>
<h3>Licenza</h3>
<p>Privacy Browser è rilasciato con <a href="https://www.gnu.org/licenses/gpl-3.0.html">Licenza GPLv3+ </a>.
<h3>Attribuzioni</h3>
<p>La lista dei server utilizzata dalla funzionalità di blocco degli annunci è tratta da <a href="https://pgl.yoyo.org/adservers/">pgl.yoyo.org</a>.
Dal momento che si tratta di una lista di domini <a href="https://www.copyright.gov/help/faq/faq-protect.html">non può essere coperta da Copyright</a>.</p>
+ <p>Privacy Browser è sviluppato con la <a href="https://developer.android.com/topic/libraries/support-library/index.html">Android Support Library</a>,
+ che è rilasciata con <a href="https://www.apache.org/licenses/LICENSE-2.0">Licenza Apache 2.0</a>.</p>
<p><img class="left" src="../en/images/privacy_browser.png"> <img class="left" src="../en/images/privacy_browser_free.png"> <img class="left" src="../en/images/warning.png"> <img class="left" src="../en/images/javascript_enabled.png">
sono state derivate da ic_security e ic_language, che fanno parte dell'<a href="https://material.io/icons/">Android Material icon set</a> e sono state rilasciate 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 © 2016 <a href="mailto:soren@stoutner.com">Soren Stoutner</a>.
// Set the content view.
setContentView(R.layout.about_coordinatorlayout);
- // We need to use the SupportActionBar from android.support.v7.app.ActionBar until the minimum API is >= 21.
- Toolbar aboutAppBar = (Toolbar) findViewById(R.id.about_toolbar);
+ // `SupportActionBar` from `android.support.v7.app.ActionBar` must be used until the minimum API is >= 21.
+ Toolbar aboutAppBar = findViewById(R.id.about_toolbar);
setSupportActionBar(aboutAppBar);
- // Display the home arrow on supportAppBar.
+ // Display the home arrow on `supportAppBar`.
final ActionBar appBar = getSupportActionBar();
- assert appBar != null;// This assert removes the incorrect warning in Android Studio on the following line that appBar might be null.
+ assert appBar != null; // This assert removes the incorrect warning in Android Studio on the following line that appBar might be null.
appBar.setDisplayHomeAsUpEnabled(true);
// Setup the ViewPager.
- ViewPager aboutViewPager = (ViewPager) findViewById(R.id.about_viewpager);
+ ViewPager aboutViewPager = findViewById(R.id.about_viewpager);
aboutViewPager.setAdapter(new aboutPagerAdapter(getSupportFragmentManager()));
// Setup the TabLayout and connect it to the ViewPager.
- TabLayout aboutTabLayout = (TabLayout) findViewById(R.id.about_tablayout);
+ TabLayout aboutTabLayout = findViewById(R.id.about_tablayout);
aboutTabLayout.setupWithViewPager(aboutViewPager);
}
/*
- * Copyright © 2015-2017 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2015-2018 Soren Stoutner <soren@stoutner.com>.
*
* Download cookie code contributed 2017 Hendrik Knackstedt. Copyright assigned to Soren Stoutner <soren@stoutner.com>.
*
// `webViewTitle` is public static so it can be accessed from `CreateBookmarkDialog` and `CreateHomeScreenShortcutDialog`. It is also used in `onCreate()`.
public static String webViewTitle;
+ // `appliedUserAgentString` is public static so it can be accessed from `ViewSourceActivity`. It is also used in `applyDomainSettings()`.
+ public static String appliedUserAgentString;
+
// `displayWebpageImagesBoolean` is public static so it can be accessed from `DomainSettingsFragment`. It is also used in `applyAppSettings()` and `applyDomainSettings()`.
public static boolean displayWebpageImagesBoolean;
// `urlTextBox` is used in `onCreate()`, `onOptionsItemSelected()`, `loadUrlFromTextBox()`, `loadUrl()`, and `highlightUrlText()`.
private EditText urlTextBox;
- // `redColorSpan` is used in `onCreate()` and `highlightUrlText()`.
+ // The color spans are used in `onCreate()` and `highlightUrlText()`.
private ForegroundColorSpan redColorSpan;
-
- // `initialGrayColorSpan` is sued in `onCreate()` and `highlightUrlText()`.
private ForegroundColorSpan initialGrayColorSpan;
-
- // `finalGrayColorSpam` is used in `onCreate()` and `highlightUrlText()`.
private ForegroundColorSpan finalGrayColorSpan;
// `adView` is used in `onCreate()` and `onConfigurationChanged()`.
appBar.setCustomView(R.layout.url_app_bar);
appBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
- // Initialize the `ForegroundColorSpans` and `StyleSpan` for highlighting `urlTextBox`. We have to use the deprecated `getColor()` until API >= 23.
+ // Initialize the foreground color spans for highlighting the URLs. We have to use the deprecated `getColor()` until API >= 23.
redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700));
initialGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500));
finalGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500));
// Get a handle for `urlTextBox`.
- urlTextBox = appBar.getCustomView().findViewById(R.id.url_edittext);
+ urlTextBox = findViewById(R.id.url_edittext);
// Remove the formatting from `urlTextBar` when the user is editing the text.
- urlTextBox.setOnFocusChangeListener((v, hasFocus) -> {
+ urlTextBox.setOnFocusChangeListener((View v, boolean hasFocus) -> {
if (hasFocus) { // The user is editing `urlTextBox`.
// Remove the highlighting.
urlTextBox.getText().removeSpan(redColorSpan);
}
});
- // Set the `Go` button on the keyboard to load the URL in `urlTextBox`.
- urlTextBox.setOnKeyListener((v, keyCode, event) -> {
+ // Set the go button on the keyboard to load the URL in `urlTextBox`.
+ urlTextBox.setOnKeyListener((View v, int keyCode, KeyEvent event) -> {
// If the event is a key-down event on the `enter` button, load the URL.
if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
// Load the URL into the mainWebView and consume the event.
// Close `bufferedReader`.
bufferedReader.close();
- } catch (IOException ioException) {
- // We're pretty sure the asset exists, so we don't need to worry about the `IOException` ever being thrown.
+ } catch (IOException e) {
+ // The asset exists, so the `IOException` will never be thrown.
}
mainWebView.setWebViewClient(new WebViewClient() {
});
}
+ // Update the progress bar.
progressBar.setProgress(progress);
+
+ // Set the visibility of the progress bar.
if (progress < 100) {
// Show the progress bar.
progressBar.setVisibility(View.VISIBLE);
registerForContextMenu(mainWebView);
// Allow the downloading of files.
- mainWebView.setDownloadListener((url, userAgent, contentDisposition, mimetype, contentLength) -> {
+ mainWebView.setDownloadListener((String url, String userAgent, String contentDisposition, String mimetype, long contentLength) -> {
// Show the `DownloadFileDialog` `AlertDialog` and name this instance `@string/download`.
AppCompatDialogFragment downloadFileDialogFragment = DownloadFileDialog.fromUrl(url, contentDisposition, contentLength);
downloadFileDialogFragment.show(getSupportFragmentManager(), getString(R.string.download));
// Run all the other default commands.
super.onPrepareOptionsMenu(menu);
- // `return true` displays the menu.
+ // Display the menu.
return true;
}
// removeAllCookies is deprecated, but it is required for API < 21.
@SuppressWarnings("deprecation")
public boolean onOptionsItemSelected(MenuItem menuItem) {
+ // Get the selected menu item ID.
int menuItemId = menuItem.getItemId();
// Set the commands that relate to the menu entries.
printManager.print(getString(R.string.privacy_browser_web_page), printDocumentAdapter, null);
return true;
+ case R.id.view_source:
+ // Launch the Vew Source activity.
+ Intent viewSourceIntent = new Intent(this, ViewSourceActivity.class);
+ startActivity(viewSourceIntent);
+ return true;
+
case R.id.add_to_homescreen:
// Show the `CreateHomeScreenShortcutDialog` `AlertDialog` and name this instance `R.string.create_shortcut`.
AppCompatDialogFragment createHomeScreenShortcutDialogFragment = new CreateHomeScreenShortcutDialog();
});
// Add a `Download Image` entry.
- menu.add(R.string.download_image).setOnMenuItemClickListener(item -> {
+ menu.add(R.string.download_image).setOnMenuItemClickListener((MenuItem item) -> {
// Show the `DownloadImageDialog` `AlertDialog` and name this instance `@string/download`.
AppCompatDialogFragment downloadImageDialogFragment = DownloadImageDialog.imageUrl(imageUrl);
downloadImageDialogFragment.show(getSupportFragmentManager(), getString(R.string.download));
});
// Add a `Download Image` entry.
- menu.add(R.string.download_image).setOnMenuItemClickListener(item -> {
+ menu.add(R.string.download_image).setOnMenuItemClickListener((MenuItem item) -> {
// Show the `DownloadImageDialog` `AlertDialog` and name this instance `@string/download`.
AppCompatDialogFragment downloadImageDialogFragment = DownloadImageDialog.imageUrl(imageUrl);
downloadImageDialogFragment.show(getSupportFragmentManager(), getString(R.string.download));
}
private void applyAppSettings() {
- // Get a handle for `sharedPreferences`. `this` references the current context.
+ // Get a handle for the shared preferences.
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
- // Store the values from `sharedPreferences` in variables.
+ // Store the values from the shared preferences in variables.
String homepageString = sharedPreferences.getString("homepage", "https://start.duckduckgo.com");
String torHomepageString = sharedPreferences.getString("tor_homepage", "https://3g2upl4pq6kufc4m.onion");
String torSearchString = sharedPreferences.getString("tor_search", "https://3g2upl4pq6kufc4m.onion/html/?q=");
// Use the selected user agent.
mainWebView.getSettings().setUserAgentString(userAgentString);
}
+
+ // Store the applied user agent string.
+ appliedUserAgentString = mainWebView.getSettings().getUserAgentString();
}
// Set a green background on `urlTextBox` to indicate that custom domain settings are being used. We have to use the deprecated `.getDrawable()` until the minimum API >= 21.
// Use the selected user agent.
mainWebView.getSettings().setUserAgentString(defaultUserAgentString);
}
+
+ // Store the applied user agent string.
+ appliedUserAgentString = mainWebView.getSettings().getUserAgentString();
}
// Set a transparent background on `urlTextBox`. We have to use the deprecated `.getDrawable()` until the minimum API >= 21.
private void highlightUrlText() {
String urlString = urlTextBox.getText().toString();
- if (urlString.startsWith("http://")) { // Highlight connections that are not encrypted.
+ if (urlString.startsWith("http://")) { // Highlight the protocol of connections that are not encrypted.
urlTextBox.getText().setSpan(redColorSpan, 0, 7, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
- } else if (urlString.startsWith("https://")) { // Highlight connections that are encrypted.
+ } else if (urlString.startsWith("https://")) { // De-emphasize the protocol of connections that are encrypted.
urlTextBox.getText().setSpan(initialGrayColorSpan, 0, 8, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
}
--- /dev/null
+/*
+ * Copyright © 2017-2018 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+ *
+ * Privacy Browser is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Privacy Browser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.stoutner.privacybrowser.activities;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.DialogFragment;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.graphics.Typeface;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.LocaleList;
+import android.preference.PreferenceManager;
+import android.support.v4.app.NavUtils;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.StyleSpan;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
+import android.webkit.CookieManager;
+import android.widget.EditText;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import com.stoutner.privacybrowser.R;
+import com.stoutner.privacybrowser.dialogs.AboutViewSourceDialog;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Locale;
+
+public class ViewSourceActivity extends AppCompatActivity {
+ // `activity` is used in `onCreate()` and `goBack()`.
+ Activity activity;
+
+ // The color spans are used in `onCreate()` and `highlightUrlText()`.
+ private ForegroundColorSpan redColorSpan;
+ private ForegroundColorSpan initialGrayColorSpan;
+ private ForegroundColorSpan finalGrayColorSpan;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ // Set the theme.
+ if (MainWebViewActivity.darkTheme) {
+ setTheme(R.style.PrivacyBrowserDark);
+ } else {
+ setTheme(R.style.PrivacyBrowserLight);
+ }
+
+ // Run the default commands.
+ super.onCreate(savedInstanceState);
+
+ // Store a handle for the current activity.
+ activity = this;
+
+ // Set the content view.
+ setContentView(R.layout.view_source_coordinatorlayout);
+
+ // `SupportActionBar` from `android.support.v7.app.ActionBar` must be used until the minimum API is >= 21.
+ Toolbar viewSourceAppBar = findViewById(R.id.view_source_toolbar);
+ setSupportActionBar(viewSourceAppBar);
+
+ // Setup the app bar.
+ final ActionBar appBar = getSupportActionBar();
+
+ // Remove the incorrect warning in Android Studio that appBar might be null.
+ assert appBar != null;
+
+ // Add the custom layout to the app bar.
+ appBar.setCustomView(R.layout.view_source_app_bar);
+ appBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
+
+ // Get a handle for the url text box.
+ EditText urlEditText = findViewById(R.id.url_edittext);
+
+ // Get the formatted URL string from the main activity.
+ String formattedUrlString = MainWebViewActivity.formattedUrlString;
+
+ // Populate the URL text box.
+ urlEditText.setText(formattedUrlString);
+
+ // Initialize the foreground color spans for highlighting the URLs. We have to use the deprecated `getColor()` until API >= 23.
+ redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700));
+ initialGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500));
+ finalGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500));
+
+ // Apply text highlighting to the URL.
+ highlightUrlText();
+
+ // Get a handle for the input method manager, which is used to hide the keyboard.
+ InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+
+ // Let Android Studio know that we aren't worried about the input method manager being null.
+ assert inputMethodManager != null;
+
+ // Remove the formatting from the URL when the user is editing the text.
+ urlEditText.setOnFocusChangeListener((View v, boolean hasFocus) -> {
+ if (hasFocus) { // The user is editing `urlTextBox`.
+ // Remove the highlighting.
+ urlEditText.getText().removeSpan(redColorSpan);
+ urlEditText.getText().removeSpan(initialGrayColorSpan);
+ urlEditText.getText().removeSpan(finalGrayColorSpan);
+ } else { // The user has stopped editing `urlTextBox`.
+ // Hide the soft keyboard.
+ inputMethodManager.hideSoftInputFromWindow(urlEditText.getWindowToken(), 0);
+
+ // Reapply the highlighting.
+ highlightUrlText();
+
+
+ }
+ });
+
+ // Set the go button on the keyboard to request new source data.
+ urlEditText.setOnKeyListener((View v, int keyCode, KeyEvent event) -> {
+ // Request new source data if the enter key was pressed.
+ if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
+ // Hide the soft keyboard.
+ inputMethodManager.hideSoftInputFromWindow(urlEditText.getWindowToken(), 0);
+
+ // Remove the focus from the URL box.
+ urlEditText.clearFocus();
+
+ // Get new source data for the current URL.
+ new GetSource().execute(urlEditText.getText().toString());
+
+ // Consume the key press.
+ return true;
+ } else {
+ // Do not consume the key press.
+ return false;
+ }
+ });
+
+ // Get the source as an `AsyncTask`.
+ new GetSource().execute(formattedUrlString);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.view_source_options_menu, menu);
+
+ // Display the menu.
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem menuItem) {
+ // Get a handle for the about alert dialog.
+ DialogFragment aboutDialogFragment = new AboutViewSourceDialog();
+
+ // Show the about alert dialog.
+ aboutDialogFragment.show(getFragmentManager(), getString(R.string.about));
+
+ // Consume the event.
+ return true;
+ }
+
+ public void goBack(View view) {
+ // Go home.
+ NavUtils.navigateUpFromSameTask(activity);
+ }
+
+ private void highlightUrlText() {
+ // Get a handle for the URL EditText.
+ EditText urlEditText = findViewById(R.id.url_edittext);
+
+ // Get the URL.
+ String urlString = urlEditText.getText().toString();
+
+ // Highlight the beginning of the URL.
+ if (urlString.startsWith("http://")) { // Highlight the protocol of connections that are not encrypted.
+ urlEditText.getText().setSpan(redColorSpan, 0, 7, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+ } else if (urlString.startsWith("https://")) { // De-emphasize the protocol of connections that are encrypted.
+ urlEditText.getText().setSpan(initialGrayColorSpan, 0, 8, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+ }
+
+ // Get the index of the `/` immediately after the domain name.
+ int endOfDomainName = urlString.indexOf("/", (urlString.indexOf("//") + 2));
+
+ // De-emphasize the text after the domain name.
+ if (endOfDomainName > 0) {
+ urlEditText.getText().setSpan(finalGrayColorSpan, endOfDomainName, urlString.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+ }
+ }
+
+ // The first `String` declares the parameters. The `Void` does not declare progress units. The last `String` contains the results.
+ // `StaticFieldLeaks` are suppressed so that Android Studio doesn't complain about running an AsyncTask in a non-static context.
+ @SuppressLint("StaticFieldLeak")
+ private class GetSource extends AsyncTask<String, Void, String> {
+ // The class variables pass information from `doInBackground()` to `onPostExecute()`.
+ SpannableStringBuilder responseMessageBuilder;
+ SpannableStringBuilder requestHeadersBuilder;
+ SpannableStringBuilder responseHeadersBuilder;
+
+ // `onPreExecute()` operates on the UI thread.
+ @Override
+ protected void onPreExecute() {
+ // Get a handle for the progress bar.
+ ProgressBar progressBar = findViewById(R.id.progress_bar);
+
+ // Make the progress bar visible.
+ progressBar.setVisibility(View.VISIBLE);
+
+ // Set the progress bar to be indeterminate.
+ progressBar.setIndeterminate(true);
+ }
+
+ @Override
+ protected String doInBackground(String... formattedUrlString) {
+ // Initialize the response body `String`.
+ String responseBodyString = "";
+
+ // Because everything relating to requesting data from a webserver can throw errors, the entire section must catch `IOExceptions`.
+ try {
+ // Get the current URL from the main activity.
+ URL url = new URL(formattedUrlString[0]);
+
+ // Open a connection to the URL. No data is actually sent at this point.
+ HttpURLConnection httpUrlConnection = (HttpURLConnection) url.openConnection();
+
+ // Instantiate the variables necessary to build the request headers.
+ requestHeadersBuilder = new SpannableStringBuilder();
+ int oldRequestHeadersBuilderLength;
+ int newRequestHeadersBuilderLength;
+
+
+ // Set the `Host` header property.
+ httpUrlConnection.setRequestProperty("Host", url.getHost());
+
+ // Add the `Host` header to the string builder and format the text.
+ if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
+ requestHeadersBuilder.append("Host", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else { // Older versions not so much.
+ oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.append("Host");
+ newRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength + 1, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ requestHeadersBuilder.append(": ");
+ requestHeadersBuilder.append(url.getHost());
+
+
+ // Set the `Connection` header property.
+ httpUrlConnection.setRequestProperty("Connection", "keep-alive");
+
+ // Add the `Connection` header to the string builder and format the text.
+ requestHeadersBuilder.append(System.getProperty("line.separator"));
+ if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
+ requestHeadersBuilder.append("Connection", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else { // Older versions not so much.
+ oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.append("Connection");
+ newRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength + 1, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ requestHeadersBuilder.append(": keep-alive");
+
+
+ // Get the current `User-Agent` string.
+ String userAgentString = MainWebViewActivity.appliedUserAgentString;
+
+ // Set the `User-Agent` header property.
+ httpUrlConnection.setRequestProperty("User-Agent", userAgentString);
+
+ // Add the `User-Agent` header to the string builder and format the text.
+ requestHeadersBuilder.append(System.getProperty("line.separator"));
+ if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
+ requestHeadersBuilder.append("User-Agent", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else { // Older versions not so much.
+ oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.append("User-Agent");
+ newRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength + 1, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ requestHeadersBuilder.append(": ");
+ requestHeadersBuilder.append(userAgentString);
+
+
+ // Set the `Upgrade-Insecure-Requests` header property.
+ httpUrlConnection.setRequestProperty("Upgrade-Insecure-Requests", "1");
+
+ // Add the `Upgrade-Insecure-Requests` header to the string builder and format the text.
+ requestHeadersBuilder.append(System.getProperty("line.separator"));
+ if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
+ requestHeadersBuilder.append("Upgrade-Insecure-Requests", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else { // Older versions not so much.
+ oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.append("Upgrade-Insecure_Requests");
+ newRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength + 1, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ requestHeadersBuilder.append(": 1");
+
+
+ // Set the `x-requested-with` header property.
+ httpUrlConnection.setRequestProperty("x-requested-with", "");
+
+ // Add the `x-requested-with` header to the string builder and format the text.
+ requestHeadersBuilder.append(System.getProperty("line.separator"));
+ if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
+ requestHeadersBuilder.append("x-requested-with", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else { // Older versions not so much.
+ oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.append("x-requested-with");
+ newRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength + 1, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ requestHeadersBuilder.append(": ");
+
+
+ // Get a handle for the shared preferences.
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
+
+ // Only populate `Do Not Track` if it is enabled.
+ if (sharedPreferences.getBoolean("do_not_track", false)) {
+ // Set the `dnt` header property.
+ httpUrlConnection.setRequestProperty("dnt", "1");
+
+ // Add the `dnt` header to the string builder and format the text.
+ requestHeadersBuilder.append(System.getProperty("line.separator"));
+ if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
+ requestHeadersBuilder.append("dnt", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else { // Older versions not so much.
+ oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.append("dnt");
+ newRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength + 1, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ requestHeadersBuilder.append(": 1");
+ }
+
+
+ // Set the `Accept` header property.
+ httpUrlConnection.setRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8");
+
+ // Add the `Accept` header to the string builder and format the text.
+ requestHeadersBuilder.append(System.getProperty("line.separator"));
+ if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
+ requestHeadersBuilder.append("Accept", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else { // Older versions not so much.
+ oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.append("Accept");
+ newRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength + 1, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ requestHeadersBuilder.append(": ");
+ requestHeadersBuilder.append("text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8");
+
+
+ // Instantiate a locale string.
+ String localeString;
+
+ // Populate the locale string.
+ if (Build.VERSION.SDK_INT >= 24) { // SDK >= 24 has a list of locales.
+ // Get the list of locales.
+ LocaleList localeList = getResources().getConfiguration().getLocales();
+
+ // Initialize a string builder to extract the locales from the list.
+ StringBuilder localesStringBuilder = new StringBuilder();
+
+ // Initialize a `q` value, which is used by `WebView` to indicate the order of importance of the languages.
+ int q = 10;
+
+ // Populate the string builder with the contents of the locales list.
+ for (int i = 0; i < localeList.size(); i++) {
+ // Append a comma if there is already an item in the string builder.
+ if (i > 0) {
+ localesStringBuilder.append(",");
+ }
+
+ // Get the indicated locale from the list.
+ localesStringBuilder.append(localeList.get(i));
+
+ // If not the first locale, append `;q=0.i`, which drops by .1 for each removal from the main locale.
+ if (q < 10) {
+ localesStringBuilder.append(";q=0.");
+ localesStringBuilder.append(q);
+ }
+
+ // Decrement `q`.
+ q--;
+ }
+
+ // Store the populated string builder in the locale string.
+ localeString = localesStringBuilder.toString();
+ } else { // SDK < 24 only has a primary locale.
+ // Store the locale in the locale string.
+ localeString = Locale.getDefault().toString();
+ }
+
+ // Set the `Accept-Language` header property.
+ httpUrlConnection.setRequestProperty("Accept-Language", localeString);
+
+ // Add the `Accept-Language` header to the string builder and format the text.
+ requestHeadersBuilder.append(System.getProperty("line.separator"));
+ if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
+ requestHeadersBuilder.append("Accept-Language", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else { // Older versions not so much.
+ oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.append("Accept-Language");
+ newRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength + 1, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ requestHeadersBuilder.append(": ");
+ requestHeadersBuilder.append(localeString);
+
+
+ // Get the cookies for the current domain.
+ String cookiesString = CookieManager.getInstance().getCookie(url.toString());
+
+ // Only process the cookies if they are not null.
+ if (cookiesString != null) {
+ // Set the `Cookie` header property.
+ httpUrlConnection.setRequestProperty("Cookie", cookiesString);
+
+ // Add the `Cookie` header to the string builder and format the text.
+ requestHeadersBuilder.append(System.getProperty("line.separator"));
+ if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
+ requestHeadersBuilder.append("Cookie", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else { // Older versions not so much.
+ oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.append("Cookie");
+ newRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength + 1, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ requestHeadersBuilder.append(": ");
+ requestHeadersBuilder.append(cookiesString);
+ }
+
+
+ // `HttpUrlConnection` sets `Accept-Encoding` to be `gzip` by default. If the property is manually set, than `HttpUrlConnection` does not process the decoding.
+ // Add the `Accept-Encoding` header to the string builder and format the text.
+ requestHeadersBuilder.append(System.getProperty("line.separator"));
+ if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
+ requestHeadersBuilder.append("Accept-Encoding", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else { // Older versions not so much.
+ oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.append("Accept-Encoding");
+ newRequestHeadersBuilderLength = requestHeadersBuilder.length();
+ requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength + 1, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ requestHeadersBuilder.append(": gzip");
+
+
+ // The actual network request is in a `try` bracket so that `disconnect()` is run in the `finally` section even if an error is encountered in the main block.
+ try {
+ // Initialize the string builders.
+ responseMessageBuilder = new SpannableStringBuilder();
+ responseHeadersBuilder = new SpannableStringBuilder();
+
+ // Get the response code, which causes the connection to the server to be made.
+ int responseCode = httpUrlConnection.getResponseCode();
+
+ // Populate the response message string builder.
+ if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
+ responseMessageBuilder.append(String.valueOf(responseCode), new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else { // Older versions not so much.
+ responseMessageBuilder.append(String.valueOf(responseCode));
+ int newLength = responseMessageBuilder.length();
+ responseMessageBuilder.setSpan(new StyleSpan(Typeface.BOLD), 0, newLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ responseMessageBuilder.append(": ");
+ responseMessageBuilder.append(httpUrlConnection.getResponseMessage());
+
+ // Initialize the iteration variable.
+ int i = 0;
+
+ // Iterate through the received header fields.
+ while (httpUrlConnection.getHeaderField(i) != null) {
+ // Add a new line if there is already information in the string builder.
+ if (i > 0) {
+ responseHeadersBuilder.append(System.getProperty("line.separator"));
+ }
+
+ // Add the header to the string builder and format the text.
+ if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
+ responseHeadersBuilder.append(httpUrlConnection.getHeaderFieldKey(i), new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else { // Older versions not so much.
+ int oldLength = responseHeadersBuilder.length();
+ responseHeadersBuilder.append(httpUrlConnection.getHeaderFieldKey(i));
+ int newLength = responseHeadersBuilder.length();
+ responseHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldLength + 1, newLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ responseHeadersBuilder.append(": ");
+ responseHeadersBuilder.append(httpUrlConnection.getHeaderField(i));
+
+ // Increment the iteration variable.
+ i++;
+ }
+
+ // Instantiate an input stream for the response body.
+ InputStream inputStream;
+
+ // Get the correct input stream based on the response code.
+ if (responseCode == 404) { // Get the error stream.
+ inputStream = new BufferedInputStream(httpUrlConnection.getErrorStream());
+ } else { // Get the response body stream.
+ inputStream = new BufferedInputStream(httpUrlConnection.getInputStream());
+ }
+
+ // Initialize the byte array output stream and the conversion buffer byte array.
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ byte[] conversionBufferByteArray = new byte[1024];
+
+ // Instantiate the variable to track the buffer length.
+ int bufferLength;
+
+ try {
+ // Attempt to read data from the input stream and store it in the conversion buffer byte array. Also store the amount of data transferred in the buffer length variable.
+ while ((bufferLength = inputStream.read(conversionBufferByteArray)) > 0) { // Proceed while the amount of data stored in the buffer is > 0.
+ // Write the contents of the conversion buffer to the byte array output stream.
+ byteArrayOutputStream.write(conversionBufferByteArray, 0, bufferLength);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ // Close the input stream.
+ inputStream.close();
+
+ // Populate the response body string with the contents of the byte array output stream.
+ responseBodyString = byteArrayOutputStream.toString();
+ } finally {
+ // Disconnect `httpUrlConnection`.
+ httpUrlConnection.disconnect();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ // Return the response body string as the result.
+ return responseBodyString;
+ }
+
+ // `onPostExecute()` operates on the UI thread.
+ @Override
+ protected void onPostExecute(String responseBodyString){
+ // Get handles for the text views.
+ TextView requestHeadersTextView = findViewById(R.id.request_headers);
+ TextView responseMessageTextView = findViewById(R.id.response_message);
+ TextView responseHeadersTextView = findViewById(R.id.response_headers);
+ TextView responseBodyTextView = findViewById(R.id.response_body);
+ ProgressBar progressBar = findViewById(R.id.progress_bar);
+
+ // Populate the text views.
+ requestHeadersTextView.setText(requestHeadersBuilder);
+ responseMessageTextView.setText(responseMessageBuilder);
+ responseHeadersTextView.setText(responseHeadersBuilder);
+ responseBodyTextView.setText(responseBodyString);
+
+ // Hide the progress bar.
+ progressBar.setIndeterminate(false);
+ progressBar.setVisibility(View.GONE);
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright © 2018 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+ *
+ * Privacy Browser is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Privacy Browser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.stoutner.privacybrowser.dialogs;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.os.Bundle;
+
+import com.stoutner.privacybrowser.R;
+import com.stoutner.privacybrowser.activities.MainWebViewActivity;
+
+public class AboutViewSourceDialog extends DialogFragment {
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ // Use a builder to create the alert dialog.
+ AlertDialog.Builder dialogBuilder;
+
+ // Set the style and the icon according to the theme.
+ if (MainWebViewActivity.darkTheme) {
+ dialogBuilder = new AlertDialog.Builder(getActivity(), R.style.PrivacyBrowserAlertDialogDark);
+ dialogBuilder.setIcon(R.drawable.about_dark);
+ } else {
+ dialogBuilder = new AlertDialog.Builder(getActivity(), R.style.PrivacyBrowserAlertDialogLight);
+ dialogBuilder.setIcon(R.drawable.about_light);
+ }
+
+ // Set an `onClick` listener on the negative button. Using `null` as the listener closes the dialog without doing anything else.
+ dialogBuilder.setNegativeButton(R.string.close, null);
+
+ // Set the title.
+ dialogBuilder.setTitle(R.string.about_view_source);
+
+ // Set the text.
+ dialogBuilder.setMessage(R.string.about_view_source_message);
+
+ // `onCreateDialog` requires the return of an `AlertDialog`.
+ return dialogBuilder.create();
+ }
+}
/*
- * Copyright © 2016-2017 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2016-2018 Soren Stoutner <soren@stoutner.com>.
*
* This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
*
// Create a drawable version of the favorite icon.
Drawable favoriteIconDrawable = new BitmapDrawable(getResources(), MainWebViewActivity.favoriteIconBitmap);
- // Use `AlertDialog.Builder` to create the `AlertDialog`.
+ // Use a builder to create the alert dialog.
AlertDialog.Builder dialogBuilder;
// Set the style according to the theme.
// Set the icon.
dialogBuilder.setIcon(favoriteIconDrawable);
- // Set an `onClick` listener on the negative button. Using `null` closes the dialog without doing anything else.
+ // Set an `onClick` listener on the negative button. Using `null` as the listener closes the dialog without doing anything else.
dialogBuilder.setNegativeButton(R.string.close, null);
// Check to see if the website is encrypted.
// Set the Layout. The parent view is `null` because it will be assigned by `AlertDialog`.
dialogBuilder.setView(layoutInflater.inflate(R.layout.unencrypted_website, null));
- // Create an `AlertDialog` from the `AlertDialog.Builder`
- final AlertDialog alertDialog = dialogBuilder.create();
-
- // Show `alertDialog`.
- alertDialog.show();
-
// `onCreateDialog` requires the return of an `AlertDialog`.
- return alertDialog;
+ return dialogBuilder.create();
} else { // Display the SSL certificate information
// Set the title.
// Set the layout. The parent view is `null` because it will be assigned by `AlertDialog`.
dialogBuilder.setView(layoutInflater.inflate(R.layout.view_ssl_certificate, null));
- // Create an `AlertDialog` from the `AlertDialog.Builder`
+ // Create an alert dialog from the builder.
final AlertDialog alertDialog = dialogBuilder.create();
- // The `AlertDialog` must be shown before items in the layout can be modified.
+ // The alert dialog must be shown before items in the layout can be modified.
alertDialog.show();
// Get handles for the `TextViews`.
- TextView domainTextView = (TextView) alertDialog.findViewById(R.id.domain);
- TextView issuedToCNameTextView = (TextView) alertDialog.findViewById(R.id.issued_to_cname);
- TextView issuedToONameTextView = (TextView) alertDialog.findViewById(R.id.issued_to_oname);
- TextView issuedToUNameTextView = (TextView) alertDialog.findViewById(R.id.issued_to_uname);
- TextView issuedByCNameTextView = (TextView) alertDialog.findViewById(R.id.issued_by_cname);
- TextView issuedByONameTextView = (TextView) alertDialog.findViewById(R.id.issued_by_oname);
- TextView issuedByUNameTextView = (TextView) alertDialog.findViewById(R.id.issued_by_uname);
- TextView startDateTextView = (TextView) alertDialog.findViewById(R.id.start_date);
- TextView endDateTextView = (TextView) alertDialog.findViewById(R.id.end_date);
+ TextView domainTextView = alertDialog.findViewById(R.id.domain);
+ TextView issuedToCNameTextView = alertDialog.findViewById(R.id.issued_to_cname);
+ TextView issuedToONameTextView = alertDialog.findViewById(R.id.issued_to_oname);
+ TextView issuedToUNameTextView = alertDialog.findViewById(R.id.issued_to_uname);
+ TextView issuedByCNameTextView = alertDialog.findViewById(R.id.issued_by_cname);
+ TextView issuedByONameTextView = alertDialog.findViewById(R.id.issued_by_oname);
+ TextView issuedByUNameTextView = alertDialog.findViewById(R.id.issued_by_uname);
+ TextView startDateTextView = alertDialog.findViewById(R.id.start_date);
+ TextView endDateTextView = alertDialog.findViewById(R.id.end_date);
// Setup the labels.
String domainLabel = getString(R.string.domain_label) + " ";
+++ /dev/null
-<!-- `about.xml` comes from the Android Material icon set, where it is called `ic_info_outline`. It is released under the Apache License 2.0. -->
-<vector
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:height="24dp"
- android:width="24dp"
- android:viewportHeight="24.0"
- android:viewportWidth="24.0" >
-
- <!-- We have to use a hard coded color until API >= 21. Then we can use `@color`. -->
- <path
- android:fillColor="#FF000000"
- android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z" />
-</vector>
--- /dev/null
+<!-- `about_dark.xml` comes from the Android Material icon set, where it is called `ic_info_outline`. It is released under the Apache License 2.0. -->
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:width="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0" >
+
+ <!-- A hard coded color must be used until API >= 21. Then `@color` may be used. -->
+ <path
+ android:fillColor="#FFE0E0E0"
+ android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z" />
+</vector>
--- /dev/null
+<!-- `about_light.xml` comes from the Android Material icon set, where it is called `ic_info_outline`. It is released under the Apache License 2.0. -->
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:width="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0" >
+
+ <!-- A hard coded color must be used until API >= 21. Then `@color` may be used. -->
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z" />
+</vector>
android:layout_marginBottom="12dp"
android:layout_gravity="bottom"
android:src="@drawable/domains"
- android:tint="?attr/iconTintColor"
+ android:tint="?attr/domainSettingsIconTintColor"
tools:ignore="contentDescription" />
<!-- `android.support.design.widget.TextInputLayout` makes the `android:hint` float above the `EditText`. -->
android:layout_marginEnd="10dp"
android:layout_gravity="center_vertical"
android:src="@drawable/user_agent_light"
- android:tint="?attr/iconTintColor"
+ android:tint="?attr/domainSettingsIconTintColor"
android:contentDescription="@string/user_agent" />
<Spinner
android:layout_marginEnd="10dp"
android:layout_gravity="center_vertical"
android:src="@drawable/font_size_light"
- android:tint="?attr/iconTintColor"
+ android:tint="?attr/domainSettingsIconTintColor"
android:contentDescription="@string/font_size" />
<Spinner
android:layout_height="match_parent"
android:layout_width="match_parent" >
- <!-- `android:fitsSystemWindows="true"` moves `root_coordinatorlayout` below the system status bar.
- When it is specified, the theme should include `<item name="android:windowTranslucentStatus">true</item>`.
- Setting the layout root to be `focusableInTouchMode` prevents `urlTextBox` from stealing focus on launch and opening the keyboard. -->
+ <!-- `android:fitsSystemWindows="true"` moves `root_coordinatorlayout` below the system status bar. When it is specified, the theme should include `<item name="android:windowTranslucentStatus">true</item>`.
+ Setting the CoordinatorLayout to be `focusableInTouchMode` prevents the URL text box from stealing focus on launch and opening the keyboard. -->
<android.support.design.widget.CoordinatorLayout
android:id="@+id/root_coordinatorlayout"
xmlns:tools="http://schemas.android.com/tools"
<!-- `android:max` changes the maximum `ProgressBar` value from 10000 to 100 to match progress percentage.
`android:layout_height="2dp"` works best for API >= 23, but `3dp` is required for visibility on API <= 22.
- `tools:ignore="UnusedAttribute"` removes the lint waring about `progressTint` and `progressBackgroundTint` not applying to API < 21. -->
+ `tools:ignore="UnusedAttribute"` removes the lint warning about `progressTint` and `progressBackgroundTint` not applying to API < 21. -->
<ProgressBar
android:id="@+id/progress_bar"
style="?android:attr/progressBarStyleHorizontal"
- android:layout_width="fill_parent"
android:layout_height="3dp"
+ android:layout_width="match_parent"
android:layout_gravity="bottom"
android:max="100"
android:progressTint="?attr/progressTintColor"
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright © 2015-2017 Soren Stoutner <soren@stoutner.com>.
+ Copyright © 2015-2018 Soren Stoutner <soren@stoutner.com>.
This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
android:layout_width="match_parent"
tools:context=".activities.MainWebViewActivity" >
- <!-- Set `@drawable/world` as the initial `favoriteIcon`.
- `layout_height` and `layout_width` of 26dp matches the `AppBar` icons. -->
+ <!-- Set `@drawable/world` as the initial as the initial favorite icon. `layout_height` and `layout_width` of 26dp matches the `AppBar` icons. -->
<ImageView
android:id="@+id/favorite_icon"
android:src="@drawable/world"
android:onClick="viewSslCertificate"
android:contentDescription="@string/favorite_icon" />
- <!-- `android:imeOptions="actionGo"` sets the keyboard to have a `go` key instead of a `new line` key.
- `android:inputType="textUri"` disables spell check in the `EditText`. -->
+ <!-- `android:imeOptions="actionGo"` sets the keyboard to have a go key instead of a new line key. `android:inputType="textUri"` disables spell check in the `EditText`. -->
<EditText
android:id="@+id/url_edittext"
android:layout_height="wrap_content"
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ Copyright © 2015-2018 Soren Stoutner <soren@stoutner.com>.
+
+ This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+
+ Privacy Browser is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Privacy Browser is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>. -->
+
+<!-- `RelativeLayout` is used instead of a `LinearLayout` because `supportAppBar` does not let `android:layout_weight="1"` cause `urlTextBox` to fill all the available space. -->
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ tools:context=".activities.ViewSourceActivity" >
+
+ <ImageView
+ android:id="@+id/back_arrow"
+ android:src="@drawable/back"
+ android:tint="?attr/viewSourceIconTintColor"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_centerVertical="true"
+ android:layout_marginEnd="14dp"
+ android:contentDescription="@string/back"
+ android:onClick="goBack" />
+
+ <!-- `android:imeOptions="actionGo"` sets the keyboard to have a `go` key instead of a `new line` key.
+ `android:inputType="textUri"` disables spell check in the `EditText`. -->
+ <EditText
+ android:id="@+id/url_edittext"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_toEndOf="@id/back_arrow"
+ android:hint="@string/url"
+ android:imeOptions="actionGo"
+ android:inputType="textUri"
+ android:selectAllOnFocus="true" />
+</RelativeLayout>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ Copyright © 2017-2018 Soren Stoutner <soren@stoutner.com>.
+
+ This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+
+ Privacy Browser is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Privacy Browser is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>. -->
+
+<!-- `android:fitsSystemWindows="true"` moves the AppBar below the status bar. When it is specified the theme should include `<item name="android:windowTranslucentStatus">true</item>` to make the status bar a transparent, darkened overlay.
+ Setting the layout root to be `focusableInTouchMode` prevents the URL toolbar from stealing focus on launch and opening the keyboard. -->
+<android.support.design.widget.CoordinatorLayout
+ android:id="@+id/view_source_coordinatorlayout"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ tools:context="com.stoutner.privacybrowser.activities.ViewSourceActivity"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:fitsSystemWindows="true"
+ android:focusable="true"
+ android:focusableInTouchMode="true" >
+
+ <!-- The `LinearLayout` with `orientation="vertical"` moves the content below the app bar layout. -->
+ <LinearLayout
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:orientation="vertical" >
+
+ <!-- The `AppBarLayout` theme has to be defined here because the activity uses a `NoActionBar` theme. -->
+ <android.support.design.widget.AppBarLayout
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:theme="@style/PrivacyBrowserAppBarLight" >
+
+ <!-- The `FrameLayout` allows the toolbar and the progress bar to occupy the same space. -->
+ <FrameLayout
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent" >
+
+ <android.support.v7.widget.Toolbar
+ android:id="@+id/view_source_toolbar"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent" />
+
+ <!-- Android automatically uses a different, skinnier drawable with padding for indeterminate horizontal progress bars in API >= 21. They make this very difficult to override. https://redmine.stoutner.com/issues/241
+ `tools:ignore="UnusedAttribute"` removes the lint warning about `progressTint` and `progressBackgroundTint` not applying to API < 21. -->
+ <ProgressBar
+ android:id="@+id/progress_bar"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:minHeight="3dp"
+ android:layout_gravity="bottom"
+ android:progressTint="?attr/progressTintColor"
+ android:progressBackgroundTint="@color/transparent"
+ android:visibility="gone"
+ tools:ignore="UnusedAttribute" />
+ </FrameLayout>
+ </android.support.design.widget.AppBarLayout>
+
+ <ScrollView
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent" >
+
+ <LinearLayout
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:orientation="vertical"
+ android:layout_margin="10dp" >
+
+ <!-- Request headers. -->
+ <TextView
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:text="@string/request_headers"
+ android:textAlignment="center"
+ android:textSize="18sp"
+ android:textColor="@color/blue_600"
+ android:textStyle="bold" />
+
+ <TextView
+ android:id="@+id/request_headers"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:textIsSelectable="true"
+ android:layout_marginBottom="8dp" />
+
+ <!-- Response message. -->
+ <TextView
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:text="@string/response_message"
+ android:textAlignment="center"
+ android:textSize="18sp"
+ android:textColor="@color/blue_600"
+ android:textStyle="bold" />
+
+ <TextView
+ android:id="@+id/response_message"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:textIsSelectable="true"
+ android:layout_marginBottom="8dp" />
+
+ <!-- Response headers. -->
+ <TextView
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:text="@string/response_headers"
+ android:textAlignment="center"
+ android:textSize="18sp"
+ android:textColor="@color/blue_600"
+ android:textStyle="bold" />
+
+ <TextView
+ android:id="@+id/response_headers"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:textIsSelectable="true"
+ android:layout_marginBottom="8dp" />
+
+ <!-- Response body. -->
+ <TextView
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:text="@string/response_body"
+ android:textAlignment="center"
+ android:textSize="18sp"
+ android:textColor="@color/blue_600"
+ android:textStyle="bold" />
+
+ <TextView
+ android:id="@+id/response_body"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:textIsSelectable="true" />
+ </LinearLayout>
+ </ScrollView>
+ </LinearLayout>
+</android.support.design.widget.CoordinatorLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright © 2015-2017 Soren Stoutner <soren@stoutner.com>.
+ Copyright © 2015-2018 Soren Stoutner <soren@stoutner.com>.
This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
+ <!-- `android:iconTint` can be used once API >= 26 instead of including a separate drawable for each theme. -->
<item
android:id="@+id/options_menu_select_all_bookmarks"
android:title="@string/select_all"
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ Copyright © 2018 Soren Stoutner <soren@stoutner.com>.
+
+ This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+
+ Privacy Browser is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Privacy Browser is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>. -->
+
+<menu
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+
+ <!-- `android:iconTint` can be used once API >= 26 instead of including a separate drawable for each theme. -->
+ <item
+ android:id="@+id/about_view_source"
+ android:title="@string/about"
+ android:orderInCategory="10"
+ android:icon="?attr/aboutIcon"
+ app:showAsAction="ifRoom" />
+</menu>
\ No newline at end of file
<item
android:id="@+id/about"
android:title="@string/about"
- android:icon="@drawable/about"
+ android:icon="@drawable/about_light"
android:orderInCategory="90" />
</group>
android:orderInCategory="120"
app:showAsAction="never" />
+ <item
+ android:id="@+id/view_source"
+ android:title="@string/view_source"
+ android:orderInCategory="130"
+ app:showAsAction="never" />
+
<item
android:id="@+id/add_to_homescreen"
android:title="@string/add_to_home_screen"
- android:orderInCategory="130"
+ android:orderInCategory="140"
app:showAsAction="never" />
<item
android:id="@+id/refresh"
android:title="@string/refresh"
- android:orderInCategory="140"
+ android:orderInCategory="150"
app:showAsAction="never" />
</menu>
\ No newline at end of file
<attr name="progressTintColor" format="reference" />
<attr name="navigationIconTintColor" format="reference" />
-
<attr name="findOnPageIconTintColor" format="reference" />
+ <attr name="domainSettingsIconTintColor" format="reference" />
+ <attr name="viewSourceIconTintColor" format="reference" />
<attr name="listSelectorDrawable" format="reference" />
- <attr name="iconTintColor" format="reference" />
-
<attr name="userAgentIcon" format="reference" />
<attr name="searchIcon" format="reference" />
<attr name="homepageIcon" format="reference" />
<attr name="selectAllIcon" format="reference" />
<attr name="editIcon" format="reference" />
<attr name="moveToFolderIcon" format="reference" />
+ <attr name="aboutIcon" format="reference" />
</resources>
\ No newline at end of file
<string name="share">Share</string>
<string name="find_on_page">Find on Page</string>
<string name="print">Print</string>
+ <string name="view_source">View Source</string>
<string name="add_to_home_screen">Add to Home Screen</string>
<string name="privacy_browser_web_page">Privacy Browser Web Page</string>
<string name="refresh">Refresh</string>
<string name="previous">Previous</string>
<string name="next">Next</string>
+ <!-- View Source. -->
+ <string name="request_headers">Request Headers</string>
+ <string name="response_message">Response Message</string>
+ <string name="response_headers">Response Headers</string>
+ <string name="response_body">Response Body</string>
+ <string name="error_body">Error Body</string>
+ <string name="about_view_source">About View Source</string>
+ <string name="about_view_source_message">Because Android’s WebView does not expose the source information, a separate request was made using system tools to gather the information displayed in this activity.
+ There may be some differences between this data and that used by the WebView in the main activity. This limitation will be removed in the 4.x series with the release of Privacy WebView.</string>
+
<!-- Create Home Screen Shortcut Alert Dialog. -->
<string name="create_shortcut">Create Shortcut</string>
<string name="shortcut_name">Shortcut name</string>
<item name="progressTintColor">@color/blue_700</item>
<item name="navigationIconTintColor">@color/blue_800</item>
<item name="findOnPageIconTintColor">@color/blue_800</item>
+ <item name="viewSourceIconTintColor">@color/black</item>
<item name="sslHeader">@color/blue_700</item>
<item name="sslTitle">@color/blue_900</item>
<item name="urlHistoryText">@color/black</item>
<item name="redText">@color/red_a700</item>
+ <item name="aboutIcon">@drawable/about_light</item>
<item name="dialogTabLayoutTheme">@style/PrivacyBrowserTabLayoutDialogLight</item>
</style>
<item name="aboutTitle">@color/blue_900</item>
<item name="aboutText">@color/blue_700</item>
<item name="aboutBackground">@color/white</item>
- <item name="iconTintColor">@color/blue_800</item>
+ <item name="domainSettingsIconTintColor">@color/blue_800</item>
<item name="deleteIcon">@drawable/delete_light</item>
<item name="addIcon">@drawable/add_light</item>
<item name="addBookmarkIcon">@drawable/create_bookmark_light</item>
<item name="colorAccent">@color/blue_700</item>
</style>
+ <style name="PrivacyBrowserProgressBar" parent="Widget.AppCompat.ProgressBar.Horizontal" >
+ <item name="android:indeterminateDrawable">@android:drawable/progress_horizontal</item>
+ </style>
+
<!-- Dark theme styles. -->
<item name="progressTintColor">@color/blue_600</item>
<item name="navigationIconTintColor">@color/blue_600</item>
<item name="findOnPageIconTintColor">@color/blue_600</item>
+ <item name="viewSourceIconTintColor">@color/gray_300</item>
<item name="sslHeader">@color/blue_400</item>
<item name="sslTitle">@color/blue_700</item>
<item name="urlHistoryText">@color/gray_200</item>
<item name="redText">@color/red_900</item>
+ <item name="aboutIcon">@drawable/about_dark</item>
</style>
<!-- `windowActionModeOverlay` makes the contextual app bar cover the support app bar. `colorPrimaryDark` goes behind the status bar, which is then darkened by the overlay.-->
<item name="aboutText">@color/blue_400</item>
<item name="aboutBackground">@color/gray_850</item>
<item name="listSelectorDrawable">@drawable/list_selector_dark</item>
- <item name="iconTintColor">@color/blue_600</item>
+ <item name="domainSettingsIconTintColor">@color/blue_600</item>
<item name="deleteIcon">@drawable/delete_dark</item>
<item name="addIcon">@drawable/add_dark</item>
<item name="addBookmarkIcon">@drawable/create_bookmark_dark</item>