From 5d3cafb4a4fbb2bf851d36f973cc1e8b23ecebab Mon Sep 17 00:00:00 2001 From: Soren Stoutner Date: Thu, 26 Dec 2019 15:15:21 -0700 Subject: [PATCH] Implement Save as Archive. https://redmine.stoutner.com/issues/188 --- app/src/main/AndroidManifest.xml | 17 +++ .../main/assets/de/guide_proxies_dark.html | 2 +- .../main/assets/de/guide_proxies_light.html | 2 +- .../main/assets/en/guide_proxies_dark.html | 2 +- .../main/assets/en/guide_proxies_light.html | 2 +- .../main/assets/es/guide_proxies_dark.html | 2 +- .../main/assets/es/guide_proxies_light.html | 2 +- .../main/assets/fr/guide_proxies_dark.html | 2 +- .../main/assets/fr/guide_proxies_light.html | 2 +- .../main/assets/it/guide_proxies_dark.html | 42 +++--- .../main/assets/it/guide_proxies_light.html | 42 +++--- .../main/assets/ru/guide_proxies_dark.html | 2 +- .../main/assets/ru/guide_proxies_light.html | 2 +- .../main/assets/tr/guide_proxies_dark.html | 2 +- .../main/assets/tr/guide_proxies_light.html | 2 +- .../activities/ImportExportActivity.java | 8 +- .../activities/LogcatActivity.java | 4 +- .../activities/MainWebViewActivity.java | 132 +++++++++++++----- ...mageDialog.java => SaveWebpageDialog.java} | 116 ++++++++++++--- .../dialogs/StoragePermissionDialog.java | 38 ++++- .../main/res/drawable/import_export_dark.xml | 6 +- .../main/res/drawable/import_export_light.xml | 6 +- .../main/res/menu/webview_options_menu.xml | 24 +++- app/src/main/res/values-de/strings.xml | 11 +- app/src/main/res/values-es/strings.xml | 11 +- app/src/main/res/values-fr/strings.xml | 11 +- app/src/main/res/values-it/strings.xml | 31 +++- app/src/main/res/values-ru/strings.xml | 11 +- app/src/main/res/values-tr/strings.xml | 4 +- app/src/main/res/values/strings.xml | 12 +- 30 files changed, 409 insertions(+), 141 deletions(-) rename app/src/main/java/com/stoutner/privacybrowser/dialogs/{SaveWebpageImageDialog.java => SaveWebpageDialog.java} (68%) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1ea8d8cc..f5f07022 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -131,6 +131,23 @@ + + + + + + + + + + + + + + + + + diff --git a/app/src/main/assets/de/guide_proxies_dark.html b/app/src/main/assets/de/guide_proxies_dark.html index 27d47e6c..5edfc551 100644 --- a/app/src/main/assets/de/guide_proxies_dark.html +++ b/app/src/main/assets/de/guide_proxies_dark.html @@ -77,7 +77,7 @@

Nutzung von Proxies

Trotz ihrer Einschränkungen können Proxies unter bestimmen Umständen durchaus nützlich sein. - Tor und I2p + Tor und I2P existieren Android-Apps, die die einfache Nutzung dieser Proxy-Netzwerke ermöglichen. Wird die Nutzung dieser Proxy-Netzwerke in Privacy Browser aktiviert, wird die App-Leiste mit einem hellblauen statt hellgrauen Hintergrund versehen. Da der Datenverkehr in solchen Fällen jedoch über mehrere Proxy-Knoten geleitet wird, diff --git a/app/src/main/assets/de/guide_proxies_light.html b/app/src/main/assets/de/guide_proxies_light.html index 9aaa3b9b..841554df 100644 --- a/app/src/main/assets/de/guide_proxies_light.html +++ b/app/src/main/assets/de/guide_proxies_light.html @@ -77,7 +77,7 @@

Nutzung von Proxies

Trotz ihrer Einschränkungen können Proxies unter bestimmen Umständen durchaus nützlich sein. - Tor und I2p + Tor und I2P existieren Android-Apps, die die einfache Nutzung dieser Proxy-Netzwerke ermöglichen. Wird die Nutzung dieser Proxy-Netzwerke in Privacy Browser aktiviert, wird die App-Leiste mit einem hellblauen statt hellgrauen Hintergrund versehen. Da der Datenverkehr in solchen Fällen jedoch über mehrere Proxy-Knoten geleitet wird, diff --git a/app/src/main/assets/en/guide_proxies_dark.html b/app/src/main/assets/en/guide_proxies_dark.html index cc723eab..a02c6e67 100644 --- a/app/src/main/assets/en/guide_proxies_dark.html +++ b/app/src/main/assets/en/guide_proxies_dark.html @@ -68,7 +68,7 @@

Using Proxies

Despite their limitations, proxies can be useful in some circumstances. - Tor and I2p + Tor and I2P have Android apps that make it easy to use their proxy networks. When proxying is turned on in Privacy Browser, the app bar will have a light blue background instead of the default light grey. Because traffic is being routed through several proxy nodes, using a layered proxy is often much slower than connecting directly to the internet.

diff --git a/app/src/main/assets/en/guide_proxies_light.html b/app/src/main/assets/en/guide_proxies_light.html index 26da4f90..2184b520 100644 --- a/app/src/main/assets/en/guide_proxies_light.html +++ b/app/src/main/assets/en/guide_proxies_light.html @@ -68,7 +68,7 @@

Using Proxies

Despite their limitations, proxies can be useful in some circumstances. - Tor and I2p + Tor and I2P have Android apps that make it easy to use their proxy networks. When proxying is turned on in Privacy Browser, the app bar will have a light blue background instead of the default light grey. Because traffic is being routed through several proxy nodes, using a layered proxy is often much slower than connecting directly to the internet.

diff --git a/app/src/main/assets/es/guide_proxies_dark.html b/app/src/main/assets/es/guide_proxies_dark.html index 21408324..beef54d8 100644 --- a/app/src/main/assets/es/guide_proxies_dark.html +++ b/app/src/main/assets/es/guide_proxies_dark.html @@ -76,7 +76,7 @@

Uso de Proxis

A pesar de sus limitaciones, los proxis pueden ser útiles en algunas circunstancias. - Tor e I2p + Tor e I2P tienen aplicaciones Android que facilitan el uso de sus redes de proxy. Cuando se activa el proxy en Navegador Privado, la barra de aplicaciones tendrá un fondo azul claro en lugar del gris claro predeterminado. Debido a que el tráfico está siendo enrutado a través de varios nodos proxy, usar un proxy en capas es a menudo mucho más lento que conectarse directamente a Internet.

diff --git a/app/src/main/assets/es/guide_proxies_light.html b/app/src/main/assets/es/guide_proxies_light.html index 6dd1ee7d..df5f1bb6 100644 --- a/app/src/main/assets/es/guide_proxies_light.html +++ b/app/src/main/assets/es/guide_proxies_light.html @@ -76,7 +76,7 @@

Uso de Proxis

A pesar de sus limitaciones, los proxis pueden ser útiles en algunas circunstancias. - Tor e I2p + Tor e I2P tienen aplicaciones Android que facilitan el uso de sus redes de proxy. Cuando se activa el proxy en Navegador Privado, la barra de aplicaciones tendrá un fondo azul claro en lugar del gris claro predeterminado. Debido a que el tráfico está siendo enrutado a través de varios nodos proxy, usar un proxy en capas es a menudo mucho más lento que conectarse directamente a Internet.

diff --git a/app/src/main/assets/fr/guide_proxies_dark.html b/app/src/main/assets/fr/guide_proxies_dark.html index 0b876b60..29384c5e 100644 --- a/app/src/main/assets/fr/guide_proxies_dark.html +++ b/app/src/main/assets/fr/guide_proxies_dark.html @@ -76,7 +76,7 @@

Utiliser des Proxies

Malgré leurs limites, les proxies peuvent être utiles dans certaines circonstances. - Tor et I2p + Tor et I2P ont des applications Android qui facilitent l'utilisation de leurs réseaux proxy. Lorsque le proxy est activé dans Privacy Browser, la barre d'application aura un fond bleu clair au lieu du gris clair par défaut. Étant donné que le trafic est acheminé via plusieurs nœuds proxy, l'utilisation d'un proxy en couches est souvent beaucoup plus lente que la connexion directe à Internet.

diff --git a/app/src/main/assets/fr/guide_proxies_light.html b/app/src/main/assets/fr/guide_proxies_light.html index 0ffb12e7..07db812b 100644 --- a/app/src/main/assets/fr/guide_proxies_light.html +++ b/app/src/main/assets/fr/guide_proxies_light.html @@ -76,7 +76,7 @@

Utiliser des Proxies

Malgré leurs limites, les proxies peuvent être utiles dans certaines circonstances. - Tor et I2p + Tor et I2P ont des applications Android qui facilitent l'utilisation de leurs réseaux proxy. Lorsque le proxy est activé dans Privacy Browser, la barre d'application aura un fond bleu clair au lieu du gris clair par défaut. Étant donné que le trafic est acheminé via plusieurs nœuds proxy, l'utilisation d'un proxy en couches est souvent beaucoup plus lente que la connexion directe à Internet.

diff --git a/app/src/main/assets/it/guide_proxies_dark.html b/app/src/main/assets/it/guide_proxies_dark.html index 2ffbb3bd..a2d68bf4 100644 --- a/app/src/main/assets/it/guide_proxies_dark.html +++ b/app/src/main/assets/it/guide_proxies_dark.html @@ -26,31 +26,34 @@ -

Proxies and Their Limits

+

I Proxy e i loro limiti

Esistono due categorie generali di cattivi soggetti che vogliono violare la privacy del web: governi maliziosi con accesso agli ISP (Internet Service Providers) e mega corporations che gestiscono social network e agenzie pubblicitarie. - Proxies like TOR (The Onion Router) and I2P (the Invisible Internet Project) are useful in protecting privacy from malicious governments (which spy on traffic in transit) - but not from mega corporations (which embed malicious code on web servers).

+ Proxy come TOR (The Onion Router) e I2P (Invisible Internet Project) sono utili per la protezione della privacy da governi maliziosi (che spiano il traffico in transito) + ma non dalle mega corporations (che inseriscono codice malizioso sui web server).

Governi Maliziosi

I governi malizionsi spesso spiano i loro cittadini per punire il dissenso o le attività di difesa dei diritti umani. Solitamente, o gestiscono loro stessi gli ISP locali oppure li obbligano a rivelare informazioni mostrando tutti gli indirizzi IP visitati da ciascun utente. - Layered proxies are designed to defeat this infringement of privacy by encrypting the traffic from a user’s device and routing it through multiple servers on the internet - before sending it on to the final destination. - This means that no individual ISP, server, or website, can know both the IP address of the user’s device and the IP address of the final web server. - Malicious governments and the ISPs they control cannot tell which web servers a user is accessing, although they can tell that the user is using a layered proxy service. - In some parts of the world, using proxies could be construed as an evidence of illegal behavior (“If you didn’t have anything to hide you wouldn’t be encrypting your traffic”) - and users could be punished because governments assume they are doing something that is prohibited. Thus, proxies can be helpful, but they aren’t a panacea.

+ I proxy a strati sono progettati per sconfiggere questa violazione di privacy crittografando il traffico dal dispositivo dell'utente e instradandolo attraverso server multipli su internet + prima di inviarlo alla destinazione finale. + Questo significa che nessun ISP individuale, server, o sito web, + può conoscere sia l'indirizzo IP del dispositivo dell'utente che l'indirizzo IP del web server finale. + I governi maliziosi e gli ISP che controllano, non possono conoscere i web server ai quali l'utente sta accedendo, sebbene sappiano che l'utente sta utilizzando un servizio di proxy a strati. + In alcune parti del mondo l'utilizzo di proxy può essere consideraro come evidenza di un comportamento illegale (“Se non hai nulla da nascondere non crittograferesti il tuo traffico”) + e gli utenti potrebbero essere perseguiti perchè il loro governo potrebbe assumere che stiano facendo qualcosa di proibito. + Per questo motivo i proxy possono essere utili ma non sono la soluzione di tutti i mali.

Mega corporations

Quando un utente si connette a un web server, il server può vedere l'indirizzo IP dell'utente. Nonostante non sia una scienza esatta, è possibile convertire l'IP in indirizzi fisici con discreta accuratezza. - Small web servers typically rely on IP addresses to identify the location of the users visiting their site. Proxies are a good solution to mask the user’s location from these servers. + I piccoli web servers solitamente si basano sull'indirizzo IP per identificare la posizione degli utenti che accedono al loro sito. + I Proxy sono una buona soluzione per mascherare la posizione dell'utente a questi server. Le mega corporations proprietarie di social media e agenzie pubblicitarie utilizzano però un intero profilo di informazioni con lo scopo di tracciare gli utenti sui dispositivi e sgli indirizzi IP. Questi profili utilizzano molte tecniche diverse per l'identificazione degli utenti, tra cui JavaScript, cookie, ID traccianti, e impronta digitale dei browser. @@ -60,26 +63,27 @@

Viene tracciato ogni sito visitato, ogni acquisto effettuato, ogni carta di credito utilizzata, ogni indirizzo di spedizione, i metadati GPS di ogni immagine che viene caricata su internet. Viene costruito il profilo per età, sesso, stato civile, indirizzo, appartenenza politica, religione, situazione familiare, animali domestici, e tutto ciò su cui possono mettere le mani. Le corporations acquistano anche i database delle transazioni con carta di credito effettuate nei negozi, per poter tracciare anche le abitudini di acquisto off-line degli utenti nei loro profili. - Because they already have much more accurate address information about a user than an IP address discloses, proxies provides no real privacy protection against mega corporations.

+ Poichè hanno già informazioni molto più accurate sull'utente rispetto a quelle fornite dall'indirizzo IP, i proxy non forniscono alcuna vera protezione della privacy contro le mega corporations.

La miglior protezione per la privacy contro le mega corporations è quella di navigare con JavaScript disabilitato, seguita dal bloccare gli annunci pubblicitari, disabilitare i cookie e il DOM storage, e utilizzare un browser di cui è difficile avere l'impronta digitale.

-

Using Proxies

+

L'utilizzo di Proxy

-

Despite their limitations, proxies can be useful in some circumstances. - Tor and I2p - have Android apps that make it easy to use their proxy networks. When proxying is turned on in Privacy Browser, the app bar will have a light blue background instead of the default light grey. - Because traffic is being routed through several proxy nodes, using a layered proxy is often much slower than connecting directly to the internet.

+

Nonostante i loro limiti, i proxy possono essere utili in alcune circostanze. + Tor e I2P + hanno app per Android che facilitano l'utilizzo delle loro reti proxy. + Quando si abilita il proxy in Privacy Browser la barra dell'app bar avrà una colorazione dello sfondo azzurra, rispetto a quella grigia di default. + Dal momento che il traffico è instradato attraverso diversi nodi proxy, l'utilizzo di un proxy a strati è spesso molto più lento di una connessione diretta a internet.

Download di File con Tor

-

When Orbot is operating in proxy mode, browsing the internet using Privacy Browser will be routed through the proxy, but file downloads will not. - This is because Privacy Browser uses Android’s builtin download manager to download files, which doesn’t have a proxy option. - Users who want to download files via Orbot need to enable its VPN mode. There is currently no way to download files through I2P.

+

Quando Orbot sta funzionando in modalità proxy, la navigazione su internet con Privacy Browser è instradata attraverso il proxy, ma il download di file no. + Questo perchè Privacy Browser utilizza il download manager nativo di Android per lo scaricamento dei file, e questo non ha l'opzione proxy. + Gli utenti che vogliono scaricare i file via Orbot devono quindi abilitare la sua modalità VPN. Attualmente non c'è ancora la possibilità di scaricare file con I2P.

diff --git a/app/src/main/assets/it/guide_proxies_light.html b/app/src/main/assets/it/guide_proxies_light.html index 7a7598ac..3719b015 100644 --- a/app/src/main/assets/it/guide_proxies_light.html +++ b/app/src/main/assets/it/guide_proxies_light.html @@ -26,31 +26,34 @@ -

Proxies and Their Limits

+

I Proxy e i loro limiti

Esistono due categorie generali di cattivi soggetti che vogliono violare la privacy del web: governi maliziosi con accesso agli ISP (Internet Service Providers) e mega corporations che gestiscono social network e agenzie pubblicitarie. - Proxies like TOR (The Onion Router) and I2P (the Invisible Internet Project) are useful in protecting privacy from malicious governments (which spy on traffic in transit) - but not from mega corporations (which embed malicious code on web servers).

+ Proxy come TOR (The Onion Router) e I2P (Invisible Internet Project) sono utili per la protezione della privacy da governi maliziosi (che spiano il traffico in transito) + ma non dalle mega corporations (che inseriscono codice malizioso sui web server).

Governi Maliziosi

I governi malizionsi spesso spiano i loro cittadini per punire il dissenso o le attività di difesa dei diritti umani. Solitamente, o gestiscono loro stessi gli ISP locali oppure li obbligano a rivelare informazioni mostrando tutti gli indirizzi IP visitati da ciascun utente. - Layered proxies are designed to defeat this infringement of privacy by encrypting the traffic from a user’s device and routing it through multiple servers on the internet - before sending it on to the final destination. - This means that no individual ISP, server, or website, can know both the IP address of the user’s device and the IP address of the final web server. - Malicious governments and the ISPs they control cannot tell which web servers a user is accessing, although they can tell that the user is using a layered proxy service. - In some parts of the world, using proxies could be construed as an evidence of illegal behavior (“If you didn’t have anything to hide you wouldn’t be encrypting your traffic”) - and users could be punished because governments assume they are doing something that is prohibited. Thus, proxies can be helpful, but they aren’t a panacea.

+ I proxy a strati sono progettati per sconfiggere questa violazione di privacy crittografando il traffico dal dispositivo dell'utente e instradandolo attraverso server multipli su internet + prima di inviarlo alla destinazione finale. + Questo significa che nessun ISP individuale, server, o sito web, + può conoscere sia l'indirizzo IP del dispositivo dell'utente che l'indirizzo IP del web server finale. + I governi maliziosi e gli ISP che controllano, non possono conoscere i web server ai quali l'utente sta accedendo, sebbene sappiano che l'utente sta utilizzando un servizio di proxy a strati. + In alcune parti del mondo l'utilizzo di proxy può essere consideraro come evidenza di un comportamento illegale (“Se non hai nulla da nascondere non crittograferesti il tuo traffico”) + e gli utenti potrebbero essere perseguiti perchè il loro governo potrebbe assumere che stiano facendo qualcosa di proibito. + Per questo motivo i proxy possono essere utili ma non sono la soluzione di tutti i mali.

Mega corporations

Quando un utente si connette a un web server, il server può vedere l'indirizzo IP dell'utente. Nonostante non sia una scienza esatta, è possibile convertire l'IP in indirizzi fisici con discreta accuratezza. - Small web servers typically rely on IP addresses to identify the location of the users visiting their site. Proxies are a good solution to mask the user’s location from these servers. + I piccoli web servers solitamente si basano sull'indirizzo IP per identificare la posizione degli utenti che accedono al loro sito. + I Proxy sono una buona soluzione per mascherare la posizione dell'utente a questi server. Le mega corporations proprietarie di social media e agenzie pubblicitarie utilizzano però un intero profilo di informazioni con lo scopo di tracciare gli utenti sui dispositivi e sgli indirizzi IP. Questi profili utilizzano molte tecniche diverse per l'identificazione degli utenti, tra cui JavaScript, cookie, ID traccianti, e impronta digitale dei browser. @@ -60,26 +63,27 @@

Viene tracciato ogni sito visitato, ogni acquisto effettuato, ogni carta di credito utilizzata, ogni indirizzo di spedizione, i metadati GPS di ogni immagine che viene caricata su internet. Viene costruito il profilo per età, sesso, stato civile, indirizzo, appartenenza politica, religione, situazione familiare, animali domestici, e tutto ciò su cui possono mettere le mani. Le corporations acquistano anche i database delle transazioni con carta di credito effettuate nei negozi, per poter tracciare anche le abitudini di acquisto off-line degli utenti nei loro profili. - Because they already have much more accurate address information about a user than an IP address discloses, proxies provides no real privacy protection against mega corporations.

+ Poichè hanno già informazioni molto più accurate sull'utente rispetto a quelle fornite dall'indirizzo IP, i proxy non forniscono alcuna vera protezione della privacy contro le mega corporations.

La miglior protezione per la privacy contro le mega corporations è quella di navigare con JavaScript disabilitato, seguita dal bloccare gli annunci pubblicitari, disabilitare i cookie e il DOM storage, e utilizzare un browser di cui è difficile avere l'impronta digitale.

-

Using Proxies

+

L'utilizzo di Proxy

-

Despite their limitations, proxies can be useful in some circumstances. - Tor and I2p - have Android apps that make it easy to use their proxy networks. When proxying is turned on in Privacy Browser, the app bar will have a light blue background instead of the default light grey. - Because traffic is being routed through several proxy nodes, using a layered proxy is often much slower than connecting directly to the internet.

+

Nonostante i loro limiti, i proxy possono essere utili in alcune circostanze. + Tor e I2P + hanno app per Android che facilitano l'utilizzo delle loro reti proxy. + Quando si abilita il proxy in Privacy Browser la barra dell'app bar avrà una colorazione dello sfondo azzurra, rispetto a quella grigia di default. + Dal momento che il traffico è instradato attraverso diversi nodi proxy, l'utilizzo di un proxy a strati è spesso molto più lento di una connessione diretta a internet.

Download di File con Tor

-

When Orbot is operating in proxy mode, browsing the internet using Privacy Browser will be routed through the proxy, but file downloads will not. - This is because Privacy Browser uses Android’s builtin download manager to download files, which doesn’t have a proxy option. - Users who want to download files via Orbot need to enable its VPN mode. There is currently no way to download files through I2P.

+

Quando Orbot sta funzionando in modalità proxy, la navigazione su internet con Privacy Browser è instradata attraverso il proxy, ma il download di file no. + Questo perchè Privacy Browser utilizza il download manager nativo di Android per lo scaricamento dei file, e questo non ha l'opzione proxy. + Gli utenti che vogliono scaricare i file via Orbot devono quindi abilitare la sua modalità VPN. Attualmente non c'è ancora la possibilità di scaricare file con I2P.

diff --git a/app/src/main/assets/ru/guide_proxies_dark.html b/app/src/main/assets/ru/guide_proxies_dark.html index 94067ab7..9141f5f3 100644 --- a/app/src/main/assets/ru/guide_proxies_dark.html +++ b/app/src/main/assets/ru/guide_proxies_dark.html @@ -69,7 +69,7 @@

Using Proxies

Despite their limitations, proxies can be useful in some circumstances. - Tor and I2p + Tor and I2P have Android apps that make it easy to use their proxy networks. When proxying is turned on in Privacy Browser, the app bar will have a light blue background instead of the default light grey. Because traffic is being routed through several proxy nodes, using a layered proxy is often much slower than connecting directly to the internet.

diff --git a/app/src/main/assets/ru/guide_proxies_light.html b/app/src/main/assets/ru/guide_proxies_light.html index 0e3946f4..15f97aa6 100644 --- a/app/src/main/assets/ru/guide_proxies_light.html +++ b/app/src/main/assets/ru/guide_proxies_light.html @@ -69,7 +69,7 @@

Using Proxies

Despite their limitations, proxies can be useful in some circumstances. - Tor and I2p + Tor and I2P have Android apps that make it easy to use their proxy networks. When proxying is turned on in Privacy Browser, the app bar will have a light blue background instead of the default light grey. Because traffic is being routed through several proxy nodes, using a layered proxy is often much slower than connecting directly to the internet.

diff --git a/app/src/main/assets/tr/guide_proxies_dark.html b/app/src/main/assets/tr/guide_proxies_dark.html index 049ed008..49d5b898 100644 --- a/app/src/main/assets/tr/guide_proxies_dark.html +++ b/app/src/main/assets/tr/guide_proxies_dark.html @@ -68,7 +68,7 @@

Using Proxies

Despite their limitations, proxies can be useful in some circumstances. - Tor and I2p + Tor and I2P have Android apps that make it easy to use their proxy networks. When proxying is turned on in Privacy Browser, the app bar will have a light blue background instead of the default light grey. Because traffic is being routed through several proxy nodes, using a layered proxy is often much slower than connecting directly to the internet.

diff --git a/app/src/main/assets/tr/guide_proxies_light.html b/app/src/main/assets/tr/guide_proxies_light.html index e4915281..d7205739 100644 --- a/app/src/main/assets/tr/guide_proxies_light.html +++ b/app/src/main/assets/tr/guide_proxies_light.html @@ -68,7 +68,7 @@

Using Proxies

Despite their limitations, proxies can be useful in some circumstances. - Tor and I2p + Tor and I2P have Android apps that make it easy to use their proxy networks. When proxying is turned on in Privacy Browser, the app bar will have a light blue background instead of the default light grey. Because traffic is being routed through several proxy nodes, using a layered proxy is often much slower than connecting directly to the internet.

diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/ImportExportActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/ImportExportActivity.java index 1c328c4d..ca4703af 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/ImportExportActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/ImportExportActivity.java @@ -117,7 +117,7 @@ public class ImportExportActivity extends AppCompatActivity implements StoragePe // Set the content view. setContentView(R.layout.import_export_coordinatorlayout); - // Use the `SupportActionBar` from `android.support.v7.app.ActionBar` until the minimum API is >= 21. + // Set the support action bar. Toolbar toolbar = findViewById(R.id.import_export_toolbar); setSupportActionBar(toolbar); @@ -130,7 +130,7 @@ public class ImportExportActivity extends AppCompatActivity implements StoragePe // Display the home arrow on the support action bar. actionBar.setDisplayHomeAsUpEnabled(true); - // Find out if we are running KitKat + // Find out if the system is running KitKat boolean runningKitKat = (Build.VERSION.SDK_INT == 19); // Find out if OpenKeychain is installed. @@ -519,7 +519,7 @@ public class ImportExportActivity extends AppCompatActivity implements StoragePe // Check if the user has previously denied the storage permission. if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { // Show a dialog explaining the request first. // Instantiate the storage permission alert dialog. - DialogFragment storagePermissionDialogFragment = new StoragePermissionDialog(); + DialogFragment storagePermissionDialogFragment = StoragePermissionDialog.displayDialog(0); // Show the storage permission alert dialog. The permission will be requested when the dialog is closed. storagePermissionDialogFragment.show(getSupportFragmentManager(), getString(R.string.storage_permission)); @@ -532,7 +532,7 @@ public class ImportExportActivity extends AppCompatActivity implements StoragePe } @Override - public void onCloseStoragePermissionDialog() { + public void onCloseStoragePermissionDialog(int type) { // Request the write external storage permission. The import/export will be run when it finishes. ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0); } diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/LogcatActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/LogcatActivity.java index 754874d8..c38181a6 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/LogcatActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/LogcatActivity.java @@ -235,7 +235,7 @@ public class LogcatActivity extends AppCompatActivity implements SaveLogcatDialo // Check if the user has previously denied the storage permission. if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { // Show a dialog explaining the request first. // Instantiate the storage permission alert dialog. - DialogFragment storagePermissionDialogFragment = new StoragePermissionDialog(); + DialogFragment storagePermissionDialogFragment = StoragePermissionDialog.displayDialog(0); // Show the storage permission alert dialog. The permission will be requested when the dialog is closed. storagePermissionDialogFragment.show(getSupportFragmentManager(), getString(R.string.storage_permission)); @@ -249,7 +249,7 @@ public class LogcatActivity extends AppCompatActivity implements SaveLogcatDialo } @Override - public void onCloseStoragePermissionDialog() { + public void onCloseStoragePermissionDialog(int type) { // Request the write external storage permission. The logcat will be saved when it finishes. ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0); } diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java index 568a316f..efa66ac9 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java @@ -133,7 +133,7 @@ import com.stoutner.privacybrowser.dialogs.FontSizeDialog; import com.stoutner.privacybrowser.dialogs.HttpAuthenticationDialog; import com.stoutner.privacybrowser.dialogs.ProxyNotInstalledDialog; import com.stoutner.privacybrowser.dialogs.PinnedMismatchDialog; -import com.stoutner.privacybrowser.dialogs.SaveWebpageImageDialog; +import com.stoutner.privacybrowser.dialogs.SaveWebpageDialog; import com.stoutner.privacybrowser.dialogs.SslCertificateErrorDialog; import com.stoutner.privacybrowser.dialogs.StoragePermissionDialog; import com.stoutner.privacybrowser.dialogs.UrlHistoryDialog; @@ -170,8 +170,9 @@ import java.util.Set; // AppCompatActivity from android.support.v7.app.AppCompatActivity must be used to have access to the SupportActionBar until the minimum API is >= 21. public class MainWebViewActivity extends AppCompatActivity implements CreateBookmarkDialog.CreateBookmarkListener, CreateBookmarkFolderDialog.CreateBookmarkFolderListener, DownloadFileDialog.DownloadFileListener, DownloadImageDialog.DownloadImageListener, DownloadLocationPermissionDialog.DownloadLocationPermissionDialogListener, EditBookmarkDialog.EditBookmarkListener, - EditBookmarkFolderDialog.EditBookmarkFolderListener, FontSizeDialog.UpdateFontSizeListener, NavigationView.OnNavigationItemSelectedListener, PinnedMismatchDialog.PinnedMismatchListener, PopulateBlocklists.PopulateBlocklistsListener, SaveWebpageImageDialog.SaveWebpageImageListener, - StoragePermissionDialog.StoragePermissionDialogListener, UrlHistoryDialog.NavigateHistoryListener, WebViewTabFragment.NewTabListener { + EditBookmarkFolderDialog.EditBookmarkFolderListener, FontSizeDialog.UpdateFontSizeListener, NavigationView.OnNavigationItemSelectedListener, PinnedMismatchDialog.PinnedMismatchListener, + PopulateBlocklists.PopulateBlocklistsListener, SaveWebpageDialog.SaveWebpageListener, StoragePermissionDialog.StoragePermissionDialogListener, UrlHistoryDialog.NavigateHistoryListener, + WebViewTabFragment.NewTabListener { // `orbotStatus` is public static so it can be accessed from `OrbotProxyHelper`. It is also used in `onCreate()`, `onResume()`, and `applyProxy()`. public static String orbotStatus = "unknown"; @@ -200,7 +201,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Start activity for result request codes. private final int FILE_UPLOAD_REQUEST_CODE = 0; - public final static int BROWSE_SAVE_WEBPAGE_IMAGE_REQUEST_CODE = 1; + public final static int BROWSE_SAVE_WEBPAGE_REQUEST_CODE = 1; // The current WebView is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onRestart()`, `onCreateContextMenu()`, `findPreviousOnPage()`, @@ -311,13 +312,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `downloadImageUrl` is used in `onCreateContextMenu()` and `onRequestPermissionResult()`. private String downloadImageUrl; - // The save website image file path string is used in `onSaveWebpageImage()` and `onRequestPermissionResult()` - private String saveWebsiteImageFilePath; + // The save webpage file path string is used in `onSaveWebpageImage()` and `onRequestPermissionResult()` + private String saveWebpageFilePath; // The permission result request codes are used in `onCreateContextMenu()`, `onCloseDownloadLocationPermissionDialog()`, `onRequestPermissionResult()`, `onSaveWebpageImage()`, // `onCloseStoragePermissionDialog()`, and `initializeWebView()`. - private final int DOWNLOAD_FILE_REQUEST_CODE = 1; - private final int DOWNLOAD_IMAGE_REQUEST_CODE = 2; + private final int DOWNLOAD_FILE_REQUEST_CODE = 0; + private final int DOWNLOAD_IMAGE_REQUEST_CODE = 1; + private final int SAVE_WEBPAGE_ARCHIVE_REQUEST_CODE = 2; private final int SAVE_WEBPAGE_IMAGE_REQUEST_CODE = 3; @Override @@ -1698,12 +1700,22 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Consume the event. return true; + case R.id.save_as_archive: + // Instantiate the save webpage archive dialog. + DialogFragment saveWebpageArchiveDialogFragment = SaveWebpageDialog.saveWebpage(SaveWebpageDialog.ARCHIVE); + + // Show the save webpage archive dialog. + saveWebpageArchiveDialogFragment.show(getSupportFragmentManager(), getString(R.string.save_webpage)); + + // Consume the event. + return true; + case R.id.save_as_image: // Instantiate the save webpage image dialog. - DialogFragment saveWebpageImageDialogFragment = new SaveWebpageImageDialog(); + DialogFragment saveWebpageImageDialogFragment = SaveWebpageDialog.saveWebpage(SaveWebpageDialog.IMAGE); // Show the save webpage image dialog. - saveWebpageImageDialogFragment.show(getSupportFragmentManager(), getString(R.string.save_as_image)); + saveWebpageImageDialogFragment.show(getSupportFragmentManager(), getString(R.string.save_webpage)); // Consume the event. return true; @@ -2680,18 +2692,32 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook downloadImageUrl = ""; break; + case SAVE_WEBPAGE_ARCHIVE_REQUEST_CODE: + // Check to see if the storage permission was granted. If the dialog was canceled the grant results will be empty. + if ((grantResults.length > 0) && (grantResults[0] == PackageManager.PERMISSION_GRANTED)) { // The storage permission was granted. + // Save the webpage archive. + currentWebView.saveWebArchive(saveWebpageFilePath); + } else { + // Display an error snackbar. + Snackbar.make(currentWebView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show(); + } + + // Reset the save webpage file path. + saveWebpageFilePath = ""; + break; + case SAVE_WEBPAGE_IMAGE_REQUEST_CODE: - // Check to see if the storage permission was granted. If the dialog was canceled the grant result will be empty. + // Check to see if the storage permission was granted. If the dialog was canceled the grant results will be empty. if ((grantResults.length > 0) && (grantResults[0] == PackageManager.PERMISSION_GRANTED)) { // The storage permission was granted. // Save the webpage image. - new SaveWebpageImage(this, currentWebView).execute(saveWebsiteImageFilePath); + new SaveWebpageImage(this, currentWebView).execute(saveWebpageFilePath); } else { // The storage permission was not granted. // Display an error snackbar. Snackbar.make(currentWebView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show(); } - // Reset the save website image file path. - saveWebsiteImageFilePath = ""; + // Reset the save webpage file path. + saveWebpageFilePath = ""; break; } } @@ -2954,22 +2980,22 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } break; - case BROWSE_SAVE_WEBPAGE_IMAGE_REQUEST_CODE: + case BROWSE_SAVE_WEBPAGE_REQUEST_CODE: // Don't do anything if the user pressed back from the file picker. if (resultCode == Activity.RESULT_OK) { // Get a handle for the save dialog fragment. - DialogFragment saveWebpageImageDialogFragment= (DialogFragment) getSupportFragmentManager().findFragmentByTag(getString(R.string.save_as_image)); + DialogFragment saveWebpageDialogFragment= (DialogFragment) getSupportFragmentManager().findFragmentByTag(getString(R.string.save_webpage)); // Only update the file name if the dialog still exists. - if (saveWebpageImageDialogFragment != null) { - // Get a handle for the save webpage image dialog. - Dialog saveWebpageImageDialog = saveWebpageImageDialogFragment.getDialog(); + if (saveWebpageDialogFragment != null) { + // Get a handle for the save webpage dialog. + Dialog saveWebpageDialog = saveWebpageDialogFragment.getDialog(); // Remove the incorrect lint warning below that the dialog might be null. - assert saveWebpageImageDialog != null; + assert saveWebpageDialog != null; // Get a handle for the file name edit text. - EditText fileNameEditText = saveWebpageImageDialog.findViewById(R.id.file_name_edittext); + EditText fileNameEditText = saveWebpageDialog.findViewById(R.id.file_name_edittext); // Instantiate the file name helper. FileNameHelper fileNameHelper = new FileNameHelper(); @@ -3135,7 +3161,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } @Override - public void onSaveWebpageImage(DialogFragment dialogFragment) { + public void onSaveWebpage(int saveType, DialogFragment dialogFragment) { // Get the dialog. Dialog dialog = dialogFragment.getDialog(); @@ -3146,12 +3172,22 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook EditText fileNameEditText = dialog.findViewById(R.id.file_name_edittext); // Get the file path string. - saveWebsiteImageFilePath = fileNameEditText.getText().toString(); + saveWebpageFilePath = fileNameEditText.getText().toString(); // Check to see if the storage permission is needed. if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { // The storage permission has been granted. - // Save the webpage image. - new SaveWebpageImage(this, currentWebView).execute(saveWebsiteImageFilePath); + //Save the webpage according to the save type. + switch (saveType) { + case SaveWebpageDialog.ARCHIVE: + // Save the webpage archive. + currentWebView.saveWebArchive(saveWebpageFilePath); + break; + + case SaveWebpageDialog.IMAGE: + // Save the webpage image. + new SaveWebpageImage(this, currentWebView).execute(saveWebpageFilePath); + break; + } } else { // The storage permission has not been granted. // Get the external private directory `File`. File externalPrivateDirectoryFile = getExternalFilesDir(null); @@ -3163,29 +3199,57 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook String externalPrivateDirectory = externalPrivateDirectoryFile.toString(); // Check to see if the file path is in the external private directory. - if (saveWebsiteImageFilePath.startsWith(externalPrivateDirectory)) { // The file path is in the external private directory. - // Save the webpage image. - new SaveWebpageImage(this, currentWebView).execute(saveWebsiteImageFilePath); + if (saveWebpageFilePath.startsWith(externalPrivateDirectory)) { // The file path is in the external private directory. + //Save the webpage according to the save type. + switch (saveType) { + case SaveWebpageDialog.ARCHIVE: + // Save the webpage archive. + currentWebView.saveWebArchive(saveWebpageFilePath); + break; + + case SaveWebpageDialog.IMAGE: + // Save the webpage image. + new SaveWebpageImage(this, currentWebView).execute(saveWebpageFilePath); + break; + } } else { // The file path is in a public directory. // Check if the user has previously denied the storage permission. if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { // Show a dialog explaining the request first. // Instantiate the storage permission alert dialog. - DialogFragment storagePermissionDialogFragment = new StoragePermissionDialog(); + DialogFragment storagePermissionDialogFragment = StoragePermissionDialog.displayDialog(saveType); // Show the storage permission alert dialog. The permission will be requested when the dialog is closed. storagePermissionDialogFragment.show(getSupportFragmentManager(), getString(R.string.storage_permission)); } else { // Show the permission request directly. - // Request the write external storage permission. The webpage image will be saved when it finishes. - ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, SAVE_WEBPAGE_IMAGE_REQUEST_CODE); + switch (saveType) { + case SaveWebpageDialog.ARCHIVE: + // Request the write external storage permission. The webpage archive will be saved when it finishes. + ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, SAVE_WEBPAGE_ARCHIVE_REQUEST_CODE); + break; + + case SaveWebpageDialog.IMAGE: + // Request the write external storage permission. The webpage image will be saved when it finishes. + ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, SAVE_WEBPAGE_IMAGE_REQUEST_CODE); + break; + } } } } } @Override - public void onCloseStoragePermissionDialog() { - // Request the write external storage permission. The webpage image will be saved when it finishes. - ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, SAVE_WEBPAGE_IMAGE_REQUEST_CODE); + public void onCloseStoragePermissionDialog(int saveType) { + switch (saveType) { + case SaveWebpageDialog.ARCHIVE: + // Request the write external storage permission. The webpage archive will be saved when it finishes. + ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, SAVE_WEBPAGE_ARCHIVE_REQUEST_CODE); + break; + + case SaveWebpageDialog.IMAGE: + // Request the write external storage permission. The webpage image will be saved when it finishes. + ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, SAVE_WEBPAGE_IMAGE_REQUEST_CODE); + break; + } } private void applyAppSettings() { diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/SaveWebpageImageDialog.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/SaveWebpageDialog.java similarity index 68% rename from app/src/main/java/com/stoutner/privacybrowser/dialogs/SaveWebpageImageDialog.java rename to app/src/main/java/com/stoutner/privacybrowser/dialogs/SaveWebpageDialog.java index a9277d9c..1c24e0be 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/SaveWebpageImageDialog.java +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/SaveWebpageDialog.java @@ -49,13 +49,17 @@ import androidx.preference.PreferenceManager; import com.stoutner.privacybrowser.R; import com.stoutner.privacybrowser.activities.MainWebViewActivity; -public class SaveWebpageImageDialog extends DialogFragment { - // Define the save webpage image listener. - private SaveWebpageImageListener saveWebpageImageListener; +public class SaveWebpageDialog extends DialogFragment { + // Define the save type constants. + public static final int ARCHIVE = 0; + public static final int IMAGE = 1; + + // Define the save webpage listener. + private SaveWebpageListener saveWebpageListener; // The public interface is used to send information back to the parent activity. - public interface SaveWebpageImageListener { - void onSaveWebpageImage(DialogFragment dialogFragment); + public interface SaveWebpageListener { + void onSaveWebpage(int saveType, DialogFragment dialogFragment); } @Override @@ -63,8 +67,25 @@ public class SaveWebpageImageDialog extends DialogFragment { // Run the default commands. super.onAttach(context); - // Get a handle for the save webpage image listener from the launching context. - saveWebpageImageListener = (SaveWebpageImageListener) context; + // Get a handle for the save webpage listener from the launching context. + saveWebpageListener = (SaveWebpageListener) context; + } + + public static SaveWebpageDialog saveWebpage(int saveType) { + // Create an arguments bundle. + Bundle argumentsBundle = new Bundle(); + + // Store the save type in the bundle. + argumentsBundle.putInt("save_type", saveType); + + // Create a new instance of the save webpage dialog. + SaveWebpageDialog saveWebpageDialog = new SaveWebpageDialog(); + + // Add the arguments bundle to the new dialog. + saveWebpageDialog.setArguments(argumentsBundle); + + // Return the new dialog. + return saveWebpageDialog; } // `@SuppressLing("InflateParams")` removes the warning about using null as the parent view group when inflating the alert dialog. @@ -72,6 +93,15 @@ public class SaveWebpageImageDialog extends DialogFragment { @Override @NonNull public Dialog onCreateDialog(Bundle savedInstanceState) { + // Get a handle for the arguments. + Bundle arguments = getArguments(); + + // Remove the incorrect lint warning that the arguments might be null. + assert arguments != null; + + // Get the save type. + int saveType = arguments.getInt("save_type"); + // Get a handle for the activity and the context. Activity activity = getActivity(); Context context = getContext(); @@ -92,15 +122,45 @@ public class SaveWebpageImageDialog extends DialogFragment { // Set the style and icon according to the theme. if (darkTheme) { + // Set the style. dialogBuilder = new AlertDialog.Builder(activity, R.style.PrivacyBrowserAlertDialogDark); - dialogBuilder.setIcon(R.drawable.images_enabled_dark); + + // Set the icon according to the save type. + switch (saveType) { + case ARCHIVE: + dialogBuilder.setIcon(R.drawable.dom_storage_cleared_dark); + break; + + case IMAGE: + dialogBuilder.setIcon(R.drawable.images_enabled_dark); + break; + } } else { + // Set the style. dialogBuilder = new AlertDialog.Builder(activity, R.style.PrivacyBrowserAlertDialogLight); - dialogBuilder.setIcon(R.drawable.images_enabled_light); + + // Set the icon according to the save type. + switch (saveType) { + case ARCHIVE: + dialogBuilder.setIcon(R.drawable.dom_storage_cleared_light); + break; + + case IMAGE: + dialogBuilder.setIcon(R.drawable.images_enabled_light); + break; + } } - // Set the title. - dialogBuilder.setTitle(R.string.save_image); + // Set the title according to the type. + switch (saveType) { + case ARCHIVE: + dialogBuilder.setTitle(R.string.save_archive); + break; + + case IMAGE: + dialogBuilder.setTitle(R.string.save_image); + break; + } // Set the view. The parent view is null because it will be assigned by the alert dialog. dialogBuilder.setView(activity.getLayoutInflater().inflate(R.layout.save_dialog, null)); @@ -111,7 +171,7 @@ public class SaveWebpageImageDialog extends DialogFragment { // Set the save button listener. dialogBuilder.setPositiveButton(R.string.save, (DialogInterface dialog, int which) -> { // Return the dialog fragment to the parent activity. - saveWebpageImageListener.onSaveWebpageImage(this); + saveWebpageListener.onSaveWebpage(saveType, this); }); // Create an alert dialog from the builder. @@ -134,16 +194,30 @@ public class SaveWebpageImageDialog extends DialogFragment { TextView storagePermissionTextView = alertDialog.findViewById(R.id.storage_permission_textview); Button saveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE); + // Create a default file name string. + String defaultFileName = ""; + + // Set the default file name according to the type. + switch (saveType) { + case ARCHIVE: + defaultFileName = getString(R.string.webpage_mht); + break; + + case IMAGE: + defaultFileName = getString(R.string.webpage_png); + break; + } + // Create a string for the default file path. String defaultFilePath; // Set the default file path according to the storage permission state. if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { // The storage permission has been granted. // Set the default file path to use the external public directory. - defaultFilePath = Environment.getExternalStorageDirectory() + "/" + getString(R.string.webpage_png); + defaultFilePath = Environment.getExternalStorageDirectory() + "/" + defaultFileName; } else { // The storage permission has not been granted. // Set the default file path to use the external private directory. - defaultFilePath = context.getExternalFilesDir(null) + "/" + getString(R.string.webpage_png); + defaultFilePath = context.getExternalFilesDir(null) + "/" + defaultFileName; } // Display the default file path. @@ -176,8 +250,16 @@ public class SaveWebpageImageDialog extends DialogFragment { // Set the intent MIME type to include all files so that everything is visible. browseIntent.setType("*/*"); - // Set the initial file name. - browseIntent.putExtra(Intent.EXTRA_TITLE, getString(R.string.webpage_png)); + // Set the initial file name according to the type. + switch (saveType) { + case ARCHIVE: + browseIntent.putExtra(Intent.EXTRA_TITLE, getString(R.string.webpage_mht)); + break; + + case IMAGE: + browseIntent.putExtra(Intent.EXTRA_TITLE, getString(R.string.webpage_png)); + break; + } // Set the initial directory if the minimum API >= 26. if (Build.VERSION.SDK_INT >= 26) { @@ -188,7 +270,7 @@ public class SaveWebpageImageDialog extends DialogFragment { browseIntent.addCategory(Intent.CATEGORY_OPENABLE); // Start the file picker. This must be started under `activity` so that the request code is returned correctly. - activity.startActivityForResult(browseIntent, MainWebViewActivity.BROWSE_SAVE_WEBPAGE_IMAGE_REQUEST_CODE); + activity.startActivityForResult(browseIntent, MainWebViewActivity.BROWSE_SAVE_WEBPAGE_REQUEST_CODE); }); // Hide the storage permission text view on API < 23 as permissions on older devices are automatically granted. diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/StoragePermissionDialog.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/StoragePermissionDialog.java index 198eb509..1ee03b8f 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/StoragePermissionDialog.java +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/StoragePermissionDialog.java @@ -39,7 +39,7 @@ public class StoragePermissionDialog extends DialogFragment { // The public interface is used to send information back to the parent activity. public interface StoragePermissionDialogListener { - void onCloseStoragePermissionDialog(); + void onCloseStoragePermissionDialog(int saveType); } @Override @@ -51,11 +51,34 @@ public class StoragePermissionDialog extends DialogFragment { storagePermissionDialogListener = (StoragePermissionDialogListener) context; } + public static StoragePermissionDialog displayDialog(int saveType) { + // Create an arguments bundle. + Bundle argumentsBundle = new Bundle(); + + // Store the save type in the bundle. + argumentsBundle.putInt("save_type", saveType); + + // Create a new instance of the storage permission dialog. + StoragePermissionDialog storagePermissionDialog = new StoragePermissionDialog(); + + // Add the arguments bundle to the new dialog. + storagePermissionDialog.setArguments(argumentsBundle); + + // Return the new dialog. + return storagePermissionDialog; + } + @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { - // Use a builder to create the alert dialog. - AlertDialog.Builder dialogBuilder; + // Get a handle for the arguments. + Bundle arguments = getArguments(); + + // Remove the incorrect lint warning that the arguments might be null. + assert arguments != null; + + // Get the save type. + int saveType = arguments.getInt("save_type"); // Get a handle for the shared preferences. SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext()); @@ -64,6 +87,9 @@ public class StoragePermissionDialog extends DialogFragment { boolean darkTheme = sharedPreferences.getBoolean("dark_theme", false); boolean allowScreenshots = sharedPreferences.getBoolean("allow_screenshots", false); + // Use a builder to create the alert dialog. + AlertDialog.Builder dialogBuilder; + // Set the style and the icon according to the theme. if (darkTheme) { dialogBuilder = new AlertDialog.Builder(getActivity(), R.style.PrivacyBrowserAlertDialogDark); @@ -79,10 +105,10 @@ public class StoragePermissionDialog extends DialogFragment { // Set the text. dialogBuilder.setMessage(R.string.storage_permission_message); - // Set an `onClick` listener on the negative button. + // Set an listener on the OK button. dialogBuilder.setNegativeButton(R.string.ok, (DialogInterface dialog, int which) -> { // Inform the parent activity that the dialog was closed. - storagePermissionDialogListener.onCloseStoragePermissionDialog(); + storagePermissionDialogListener.onCloseStoragePermissionDialog(saveType); }); // Create an alert dialog from the builder. @@ -97,7 +123,7 @@ public class StoragePermissionDialog extends DialogFragment { alertDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE); } - // `onCreateDialog()` requires the return of an `AlertDialog`. + // Return the alert dialog. return alertDialog; } } \ No newline at end of file diff --git a/app/src/main/res/drawable/import_export_dark.xml b/app/src/main/res/drawable/import_export_dark.xml index 21cdea86..1847f4fc 100644 --- a/app/src/main/res/drawable/import_export_dark.xml +++ b/app/src/main/res/drawable/import_export_dark.xml @@ -1,4 +1,4 @@ - + = 21. Then `@color` may be used. --> - + \ No newline at end of file diff --git a/app/src/main/res/drawable/import_export_light.xml b/app/src/main/res/drawable/import_export_light.xml index 602cf376..119da8c8 100644 --- a/app/src/main/res/drawable/import_export_light.xml +++ b/app/src/main/res/drawable/import_export_light.xml @@ -1,4 +1,4 @@ - + = 21. Then `@color` may be used. --> - + \ No newline at end of file diff --git a/app/src/main/res/menu/webview_options_menu.xml b/app/src/main/res/menu/webview_options_menu.xml index 744873fc..28aed273 100644 --- a/app/src/main/res/menu/webview_options_menu.xml +++ b/app/src/main/res/menu/webview_options_menu.xml @@ -315,8 +315,7 @@ android:id="@+id/font_size" android:title="@string/font_size" android:orderInCategory="1070" - app:showAsAction="never" > - + app:showAsAction="never" /> + app:showAsAction="never" > + + + + + + + Auf Seite finden Drucken Privacy Browser-Website - Als Grafik speichern + Speichern + Als Grafik speichern Quelltext anzeigen Zur Startseite hinzufügen Teilen @@ -246,7 +247,6 @@ Ordner bearbeiten In Ordner verschieben Verschieben - Speichern Ausgewählt: @@ -404,6 +404,8 @@ I2P ist nicht installiert Der I2P-Proxy kann erst nach Installation der I2P-App genutzt werden. Warte auf die Verbindung zum Orbot-Proxy. + Die benutzerdefinierte Proxy-URL ist ungültig. + SOCKS-Proxies funktionieren nicht unter Android KitKat. Über Privacy Browser @@ -548,7 +550,12 @@ I2P Benutzerdefiniert + Keiner - direkte Verbindung zum Internet. + Tor - Verbidnung über socks://localhost:9050. + Tor - Verbidnung über http://localhost:8118. + I2P - Verbindung über http://localhost:4444. Benutzerdefinierter Proxy + Benutzerdefinierte Proxy-URL Vollbild-Modus Vollbild-Browser-Modus Doppel-Tippen, um zwischen normalem und Vollbild-Modus umzuschalten. diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index ff1803e8..983c125a 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -173,7 +173,8 @@ Buscar en página Imprimir Página web de Navegador Privado - Guardar como imagen + Guardar + Guardar como imagen Añadir a la ventana de inicio Ver la fuente Compartir @@ -243,7 +244,6 @@ Editar carpeta Mover a carpeta Mover - Guardar Seleccionados: @@ -403,6 +403,8 @@ I2P No Instalado El proxy a través de I2P no funcionará a menos que la aplicación I2P esté instalada. Esperando a que Orbot se conecte. + La URL del proxy personalizado no es válida. + SOCKS proxies do not work on Android KitKat. Acerca de Navegador Privado @@ -533,8 +535,13 @@ Yahoo - Javascript habilitado Personalizado + Ninguno - conectar directamente a Internet. + Tor - conectar a través de socks://localhost:9050. + Tor - conectar a través de http://localhost:8118. + I2P - conectar a través de http://localhost:4444. URL personalizado de búsqueda URL personalizado + URL personalizada del proxy Proxy Ninguno Tor diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 2a6668ea..fc9a20ed 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -174,7 +174,8 @@ Chercher sur la page Imprimer Site Web de Privacy Browser - Sauvergarder comme image + Sauvegarder + Sauvergarder comme image Ajouter à l\'écran d\'accueil Voir Source Partager @@ -243,7 +244,6 @@ Editer dossier Déplacer vers dossier Déplacer - Sauvegarder Selectionnés : @@ -403,6 +403,8 @@ I2P non installé Le proxy via I2P ne fonctionnera que si l\'application I2P est installée. En attente de la connexion d\'Orbot. + L\'URL du proxy personnalisé n\'est pas valide. + Les proxys de type SOCKS ne fonctionnent pas sur Android KitKat. À propos @@ -546,7 +548,12 @@ I2P Personnalisé + Aucun - Connectez-vous directement à Internet. + Tor - Connectez-vous via socks://localhost:9050. + Tor - Connectez-vous via http://localhost:8118. + I2P - Connectez-vous via http://localhost:4444. Proxy personnalisé + URL personnalisée du proxy Plein écran Navigation plein écran Appuyez deux fois pour basculer en mode de navigation en plein écran. diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index fdb96623..757c3ee0 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -173,7 +173,8 @@ Cerca nella pagina Stampa Pagina web di Privacy Browser - Salva come Immagine + Salva + Salva come Immagine Aggiungi collegamento Visualizza sorgente Condividi @@ -242,7 +243,6 @@ Modifica Cartella Sposta nella Cartella Sposta - Salva Selezionato: @@ -387,6 +387,7 @@ Descrizione Archiviazione Locale Certificati SSL + Proxy Tracciamento utenti @@ -395,8 +396,14 @@ Nel caso in cui il permesso sia negato sarà utilizzata la cartella di download dell\'applicazione. OK - + + Orbot Non Installato + Il Proxy con Orbot non funziona se non è installata la app Orbot. + I2P Non Installato + Il Proxy con I2P non funziona se non è installata la app I2P. In attesa della connessione di Orbot. + La URL del proxy personalizzato non è valida. + I proxy SOCKS non funzionano con Android KitKat. Informazioni su Privacy Browser @@ -417,6 +424,7 @@ Provider di WebView: Versione di WebView: Orbot: + I2P: OpenKeychain: EasyList: EasyPrivacy: @@ -529,6 +537,23 @@ Ricerca personalizzata URL Personalizzata + Proxy + Nessuno + Tor + I2P + Personalizzato + + Nessuno + Tor + I2P + Personalizzato + + Nessuno - connessione diretta a internet. + Tor - connessione con socks://localhost:9050. + Tor - connessione con http://localhost:8118. + I2P - connessione con http://localhost:4444. + Proxy personalizzato + URL personalizzata del Proxy Schermo intero Navigazione a schermo intero Toccare due volte per avviare la navigazione a schermo intero. diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 83fdac30..6ee136bb 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -170,7 +170,8 @@ Найти на странице Печать Privacy Browser веб-страница - Сохранить как изображение + Сохранить + Сохранить как изображение Добавить на главный экран Просмотр исходного кода Поделиться @@ -239,7 +240,6 @@ Изменить папку Переместить в папку Переместить - Сохранить выбраны: @@ -397,6 +397,8 @@ I2P не установлен Прокси через I2P работать не будет, если приложение I2P не установлено. Ожидание подключения Orbot. + URL пользовательского прокси недействителен. + SOCKS-прокси не работает на Android KitKat. О Privacy Browser @@ -540,7 +542,12 @@ I2P Пользовательский + Нет - подключиться к интернету напрямую. + Tor - connect through socks://localhost:9050. + Tor - подключиться через http://localhost:8118. + I2P - подключиться через http://localhost:4444. Пользовательский прокси + URL пользовательского прокси Во весь экран Полноэкранный режим просмотра Двойное касание переключает режим просмотра. diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 73416b76..c215b005 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -170,7 +170,8 @@ Sayfada bul Yazdır Privacy Browser Web Sayfası - Resmi farklı kaydet + Kaydet + Resmi farklı kaydet Ana ekrana ekle Kaynağı görüntüle Paylaş @@ -237,7 +238,6 @@ Klasörü düzenle Klasöre taşı Taşı - Kaydet Seçili: diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5e8575cf..c486baf3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -177,7 +177,9 @@ Find on Page Print Privacy Browser Web Page - Save as Image + Save + Save as Archive + Save as Image Add to Home Screen View Source Share @@ -204,8 +206,11 @@ Previous Next - + + Save Webpage + Save Archive Save Image + Webpage.mht Webpage.png Saving image… Image saved. @@ -247,7 +252,6 @@ Edit Folder Move to Folder Move - Save Selected: @@ -603,7 +607,7 @@ None - connect directly to the internet. Tor - connect through socks://localhost:9050. - Tor - connect through http://localhost:8118 + Tor - connect through http://localhost:8118. I2P - connect through http://localhost:4444. Custom proxy Proxy custom URL -- 2.43.0