From 33bd447a83bd3d763ee26bbb3a3f4adb074776ed Mon Sep 17 00:00:00 2001
From: Soren Stoutner
Date: Tue, 12 Feb 2019 21:33:26 -0700
Subject: [PATCH] Add a logcat activity.
https://redmine.stoutner.com/issues/264
---
.idea/assetWizardSettings.xml | 7 +-
.idea/dictionaries/soren.xml | 1 +
app/src/main/AndroidManifest.xml | 18 +-
.../main/assets/de/about_licenses_dark.html | 2 +
.../main/assets/de/about_licenses_light.html | 2 +
.../main/assets/en/about_licenses_dark.html | 2 +
.../main/assets/en/about_licenses_light.html | 2 +
.../main/assets/es/about_licenses_dark.html | 2 +
.../main/assets/es/about_licenses_light.html | 2 +
.../es/guide_ssl_certificates_dark.html | 4 +-
.../es/guide_ssl_certificates_light.html | 4 +-
.../main/assets/it/about_licenses_dark.html | 2 +
.../main/assets/it/about_licenses_light.html | 2 +
.../it/guide_ssl_certificates_dark.html | 2 +-
.../it/guide_ssl_certificates_light.html | 2 +-
.../main/assets/ru/about_licenses_dark.html | 2 +
.../main/assets/ru/about_licenses_light.html | 2 +
.../ru/guide_ssl_certificates_dark.html | 4 +-
.../ru/guide_ssl_certificates_light.html | 4 +-
.../assets/shared_images/file_copy_dark.png | Bin 0 -> 1220 bytes
.../assets/shared_images/file_copy_light.png | Bin 0 -> 1121 bytes
.../main/assets/shared_images/save_dark.png | Bin 0 -> 1432 bytes
.../main/assets/shared_images/save_light.png | Bin 0 -> 1245 bytes
.../main/assets/tr/about_licenses_dark.html | 2 +
.../main/assets/tr/about_licenses_light.html | 2 +
.../activities/ImportExportActivity.java | 81 ++--
.../activities/LogcatActivity.java | 448 ++++++++++++++++++
.../activities/MainWebViewActivity.java | 99 ++--
.../activities/RequestsActivity.java | 4 +-
.../activities/ViewSourceActivity.java | 6 +-
.../dialogs/EditBookmarkDialog.java | 24 +-
.../dialogs/PinnedMismatchDialog.java | 5 +-
.../dialogs/SaveLogcatDialog.java | 197 ++++++++
...alog.java => StoragePermissionDialog.java} | 17 +-
app/src/main/res/drawable/bug.xml | 13 +
app/src/main/res/drawable/clear_dark.xml | 13 +
app/src/main/res/drawable/clear_light.xml | 13 +
app/src/main/res/drawable/cookies_warning.xml | 2 +-
app/src/main/res/drawable/copy_dark.xml | 18 +
app/src/main/res/drawable/copy_light.xml | 18 +
app/src/main/res/drawable/save_dark.xml | 18 +
.../main/res/drawable/save_dialog_dark.xml | 18 +
.../main/res/drawable/save_dialog_light.xml | 18 +
app/src/main/res/drawable/save_light.xml | 18 +
app/src/main/res/drawable/select_all_dark.xml | 4 +-
.../main/res/drawable/select_all_light.xml | 4 +-
.../drawable/ssl_certificate_enabled_dark.xml | 4 +-
.../ssl_certificate_enabled_light.xml | 4 +-
.../res/layout-w900dp/bookmarks_drawer.xml | 28 +-
.../layout-w900dp/domains_list_fragment.xml | 4 +-
.../main/res/layout/appbar_spinner_item.xml | 1 -
app/src/main/res/layout/bookmarks_drawer.xml | 24 +-
.../import_export_coordinatorlayout.xml | 2 +-
.../res/layout/logcat_coordinatorlayout.xml | 69 +++
app/src/main/res/layout/navigation_header.xml | 2 +-
.../requests_appbar_spinner_dropdown_item.xml | 35 ++
.../layout/requests_appbar_spinner_item.xml | 31 ++
.../res/layout/requests_coordinatorlayout.xml | 6 +-
.../main/res/layout/save_logcat_dialog.xml | 72 +++
app/src/main/res/menu/logcat_options_menu.xml | 48 ++
.../main/res/menu/webview_navigation_menu.xml | 16 +-
app/src/main/res/values-de/strings.xml | 7 +-
app/src/main/res/values-es/strings.xml | 5 +-
app/src/main/res/values-it/strings.xml | 4 -
app/src/main/res/values-ru/strings.xml | 5 +-
app/src/main/res/values-tr/strings.xml | 5 +-
app/src/main/res/values-v21/styles.xml | 6 +
app/src/main/res/values/attrs.xml | 19 +-
app/src/main/res/values/strings.xml | 20 +-
app/src/main/res/values/styles.xml | 6 +
70 files changed, 1334 insertions(+), 197 deletions(-)
create mode 100644 app/src/main/assets/shared_images/file_copy_dark.png
create mode 100644 app/src/main/assets/shared_images/file_copy_light.png
create mode 100644 app/src/main/assets/shared_images/save_dark.png
create mode 100644 app/src/main/assets/shared_images/save_light.png
create mode 100644 app/src/main/java/com/stoutner/privacybrowser/activities/LogcatActivity.java
create mode 100644 app/src/main/java/com/stoutner/privacybrowser/dialogs/SaveLogcatDialog.java
rename app/src/main/java/com/stoutner/privacybrowser/dialogs/{ImportExportStoragePermissionDialog.java => StoragePermissionDialog.java} (82%)
create mode 100644 app/src/main/res/drawable/bug.xml
create mode 100644 app/src/main/res/drawable/clear_dark.xml
create mode 100644 app/src/main/res/drawable/clear_light.xml
create mode 100644 app/src/main/res/drawable/copy_dark.xml
create mode 100644 app/src/main/res/drawable/copy_light.xml
create mode 100644 app/src/main/res/drawable/save_dark.xml
create mode 100644 app/src/main/res/drawable/save_dialog_dark.xml
create mode 100644 app/src/main/res/drawable/save_dialog_light.xml
create mode 100644 app/src/main/res/drawable/save_light.xml
create mode 100644 app/src/main/res/layout/logcat_coordinatorlayout.xml
create mode 100644 app/src/main/res/layout/requests_appbar_spinner_dropdown_item.xml
create mode 100644 app/src/main/res/layout/requests_appbar_spinner_item.xml
create mode 100644 app/src/main/res/layout/save_logcat_dialog.xml
create mode 100644 app/src/main/res/menu/logcat_options_menu.xml
diff --git a/.idea/assetWizardSettings.xml b/.idea/assetWizardSettings.xml
index e167cf29..7958d0d2 100644
--- a/.idea/assetWizardSettings.xml
+++ b/.idea/assetWizardSettings.xml
@@ -68,7 +68,7 @@
-
+
@@ -78,10 +78,9 @@
-
-
-
+
+
diff --git a/.idea/dictionaries/soren.xml b/.idea/dictionaries/soren.xml
index 87f9d7ec..e0dfdc07 100644
--- a/.idea/dictionaries/soren.xml
+++ b/.idea/dictionaries/soren.xml
@@ -93,6 +93,7 @@
licensors
linearlayout
listview
+ logcats
logins
lossless
macos
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index c018200e..9e443d8f 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,7 +1,7 @@
+
+
+
edit.
expand_less.
expand_more.
+ file_copy.
file_download.
find_in_page.
folder.
@@ -132,6 +133,7 @@
new releases.
question_answer.
refresh.
+ save.
search.
select_all.
settings.
diff --git a/app/src/main/assets/de/about_licenses_light.html b/app/src/main/assets/de/about_licenses_light.html
index 74116fa6..6592f4f3 100644
--- a/app/src/main/assets/de/about_licenses_light.html
+++ b/app/src/main/assets/de/about_licenses_light.html
@@ -112,6 +112,7 @@
edit.
expand_less.
expand_more.
+ file_copy.
file_download.
find_in_page.
folder.
@@ -132,6 +133,7 @@
new releases.
question_answer.
refresh.
+ save.
search.
select_all.
settings.
diff --git a/app/src/main/assets/en/about_licenses_dark.html b/app/src/main/assets/en/about_licenses_dark.html
index 99cb2c6e..da0defc4 100644
--- a/app/src/main/assets/en/about_licenses_dark.html
+++ b/app/src/main/assets/en/about_licenses_dark.html
@@ -110,6 +110,7 @@
edit.
expand_less.
expand_more.
+ file_copy.
file_download.
find_in_page.
folder.
@@ -130,6 +131,7 @@
new releases.
question_answer.
refresh.
+ save.
search.
select_all.
settings.
diff --git a/app/src/main/assets/en/about_licenses_light.html b/app/src/main/assets/en/about_licenses_light.html
index 8fd7c674..5ecac0a9 100644
--- a/app/src/main/assets/en/about_licenses_light.html
+++ b/app/src/main/assets/en/about_licenses_light.html
@@ -111,6 +111,7 @@
edit.
expand_less.
expand_more.
+ file_copy.
file_download.
find_in_page.
folder.
@@ -131,6 +132,7 @@
new releases.
question_answer.
refresh.
+ save.
search.
select_all.
settings.
diff --git a/app/src/main/assets/es/about_licenses_dark.html b/app/src/main/assets/es/about_licenses_dark.html
index 2f92b563..1c36c979 100644
--- a/app/src/main/assets/es/about_licenses_dark.html
+++ b/app/src/main/assets/es/about_licenses_dark.html
@@ -115,6 +115,7 @@
edit.
expand_less.
expand_more.
+ file_copy.
file_download.
find_in_page.
folder.
@@ -135,6 +136,7 @@
new releases.
question_answer.
refresh.
+ save.
search.
select_all.
settings.
diff --git a/app/src/main/assets/es/about_licenses_light.html b/app/src/main/assets/es/about_licenses_light.html
index 1a3aa312..1c8476f2 100644
--- a/app/src/main/assets/es/about_licenses_light.html
+++ b/app/src/main/assets/es/about_licenses_light.html
@@ -116,6 +116,7 @@
edit.
expand_less.
expand_more.
+ file_copy.
file_download.
find_in_page.
folder.
@@ -136,6 +137,7 @@
new releases.
question_answer.
refresh.
+ save.
search.
select_all.
settings.
diff --git a/app/src/main/assets/es/guide_ssl_certificates_dark.html b/app/src/main/assets/es/guide_ssl_certificates_dark.html
index 1780234d..9a704795 100644
--- a/app/src/main/assets/es/guide_ssl_certificates_dark.html
+++ b/app/src/main/assets/es/guide_ssl_certificates_dark.html
@@ -41,8 +41,8 @@
Los certificados SSL expiran en una fecha especificada, por lo que incluso los certificados SSL fijados necesitarán legÃtimamente ser actualizados de vez en cuando.
Como regla general, fijar los certificados SSL probablemente no sea necesario en la mayorÃa de los casos.
- Pero para aquellos que sospechan que organizaciones poderosas puedan estar aputando hacia ellos, la fijación de certificados SSL puede detectar y frustar un ataque MITM.
- Privacy Browser also has the ability to pin IP addresses.
+ Pero para aquellos que sospechan que organizaciones poderosas puedan estar apuntando hacia ellos, la fijación de certificados SSL puede detectar y frustar un ataque MITM.
+ Navegador Privado también tiene la capacidad de fijar direcciones IP.
diff --git a/app/src/main/assets/es/guide_ssl_certificates_light.html b/app/src/main/assets/es/guide_ssl_certificates_light.html
index 20097fab..02dfecb1 100644
--- a/app/src/main/assets/es/guide_ssl_certificates_light.html
+++ b/app/src/main/assets/es/guide_ssl_certificates_light.html
@@ -41,8 +41,8 @@
Los certificados SSL expiran en una fecha especificada, por lo que incluso los certificados SSL fijados necesitarán legÃtimamente ser actualizados de vez en cuando.
Como regla general, fijar los certificados SSL probablemente no sea necesario en la mayorÃa de los casos.
- Pero para aquellos que sospechan que organizaciones poderosas puedan estar aputando hacia ellos, la fijación de certificados SSL puede detectar y frustar un ataque MITM.
- Privacy Browser also has the ability to pin IP addresses.
+ Pero para aquellos que sospechan que organizaciones poderosas puedan estar apuntando hacia ellos, la fijación de certificados SSL puede detectar y frustar un ataque MITM.
+ Navegador Privado también tiene la capacidad de fijar direcciones IP.
diff --git a/app/src/main/assets/it/about_licenses_dark.html b/app/src/main/assets/it/about_licenses_dark.html
index caba0d32..695c33dd 100644
--- a/app/src/main/assets/it/about_licenses_dark.html
+++ b/app/src/main/assets/it/about_licenses_dark.html
@@ -119,6 +119,7 @@
edit.
expand_less.
expand_more.
+ file_copy.
file_download.
find_in_page.
folder.
@@ -139,6 +140,7 @@
new releases.
question_answer.
refresh.
+ save.
search.
select_all.
settings.
diff --git a/app/src/main/assets/it/about_licenses_light.html b/app/src/main/assets/it/about_licenses_light.html
index 3b92019e..f8698477 100644
--- a/app/src/main/assets/it/about_licenses_light.html
+++ b/app/src/main/assets/it/about_licenses_light.html
@@ -120,6 +120,7 @@
edit.
expand_less.
expand_more.
+ file_copy.
file_download.
find_in_page.
folder.
@@ -140,6 +141,7 @@
new releases.
question_answer.
refresh.
+ save.
search.
select_all.
settings.
diff --git a/app/src/main/assets/it/guide_ssl_certificates_dark.html b/app/src/main/assets/it/guide_ssl_certificates_dark.html
index b9cdc1b9..f6713631 100644
--- a/app/src/main/assets/it/guide_ssl_certificates_dark.html
+++ b/app/src/main/assets/it/guide_ssl_certificates_dark.html
@@ -42,7 +42,7 @@
I certificati SSL scadono in corrispondenza di una data specifica, così anche i certificati che sono stati appuntati dovranno essere aggiornati regolarmente.
Come regola generale, nella maggioranza dei casi, appuntare un certificato SSL non dovrebbe essere necessario.
Per coloro che sospettano però di essere sorvegliati da qualche organizzazione, appuntare il certificato SSL può permettere di scoprire e sventare un attacco "MITM".
- Privacy Browser also has the ability to pin IP addresses.
+ Privacy Browser permette anche di appuntare gli indirizzi IP.
diff --git a/app/src/main/assets/it/guide_ssl_certificates_light.html b/app/src/main/assets/it/guide_ssl_certificates_light.html
index 98da47b6..88b6e16e 100644
--- a/app/src/main/assets/it/guide_ssl_certificates_light.html
+++ b/app/src/main/assets/it/guide_ssl_certificates_light.html
@@ -42,7 +42,7 @@
I certificati SSL scadono in corrispondenza di una data specifica, così anche i certificati che sono stati appuntati dovranno essere aggiornati regolarmente.
Come regola generale, nella maggioranza dei casi, appuntare un certificato SSL non dovrebbe essere necessario.
Per coloro che sospettano però di essere sorvegliati da qualche organizzazione, appuntare il certificato SSL può permettere di scoprire e sventare un attacco "MITM".
- Privacy Browser also has the ability to pin IP addresses.
+ Privacy Browser permette anche di appuntare gli indirizzi IP.
diff --git a/app/src/main/assets/ru/about_licenses_dark.html b/app/src/main/assets/ru/about_licenses_dark.html
index d2ed014e..fb207f62 100644
--- a/app/src/main/assets/ru/about_licenses_dark.html
+++ b/app/src/main/assets/ru/about_licenses_dark.html
@@ -113,6 +113,7 @@
edit.
expand_less.
expand_more.
+ file_copy.
file_download.
find_in_page.
folder.
@@ -133,6 +134,7 @@
new releases.
question_answer.
refresh.
+ save.
search.
select_all.
settings.
diff --git a/app/src/main/assets/ru/about_licenses_light.html b/app/src/main/assets/ru/about_licenses_light.html
index 54089956..9770ee6d 100644
--- a/app/src/main/assets/ru/about_licenses_light.html
+++ b/app/src/main/assets/ru/about_licenses_light.html
@@ -113,6 +113,7 @@
edit.
expand_less.
expand_more.
+ file_copy.
file_download.
find_in_page.
folder.
@@ -133,6 +134,7 @@
new releases.
question_answer.
refresh.
+ save.
search.
select_all.
settings.
diff --git a/app/src/main/assets/ru/guide_ssl_certificates_dark.html b/app/src/main/assets/ru/guide_ssl_certificates_dark.html
index dcff620d..9cb32b4b 100644
--- a/app/src/main/assets/ru/guide_ssl_certificates_dark.html
+++ b/app/src/main/assets/ru/guide_ssl_certificates_dark.html
@@ -40,8 +40,8 @@
СÑок дейÑÑÐ²Ð¸Ñ ÑеÑÑиÑикаÑов SSL иÑÑÐµÐºÐ°ÐµÑ Ð² ÑказаннÑÑ Ð´Ð°ÑÑ, поÑÑÐ¾Ð¼Ñ Ð´Ð°Ð¶Ðµ закÑепленнÑе ÑеÑÑиÑикаÑÑ SSL бÑдÑÑ Ð¿ÐµÑиодиÑеÑки обновлÑÑÑÑÑ.
Ðак пÑавило, закÑепление ÑеÑÑиÑикаÑов SSL в болÑÑинÑÑве ÑлÑÑаев не ÑÑебÑеÑÑÑ.
- Ðо Ð´Ð»Ñ ÑеÑ
, кÑо подозÑеваеÑ, ÑÑо за ними ведеÑÑÑ Ð½Ð°Ð±Ð»Ñдение, закÑепление ÑеÑÑиÑикаÑа SSL Ð¼Ð¾Ð¶ÐµÑ Ð¾Ð±Ð½Ð°ÑÑжиÑÑ Ð¸ помеÑаÑÑ Ð°Ñаке MITM.
- Privacy Browser also has the ability to pin IP addresses.
+ Ðо Ð´Ð»Ñ Ñого, кÑо подозÑеваеÑ, ÑÑо за ним ведеÑÑÑ Ð½Ð°Ð±Ð»Ñдение, закÑепление ÑеÑÑиÑикаÑа SSL Ð¿Ð¾Ð¼Ð¾Ð¶ÐµÑ Ð¾Ð±Ð½Ð°ÑÑжиÑÑ Ð¸ помеÑаÑÑ Ð°Ñаке MITM.
+ Privacy Browser Ñакже Ð¸Ð¼ÐµÐµÑ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ÑÑÑ Ð·Ð°ÐºÑÐµÐ¿Ð»ÐµÐ½Ð¸Ñ IP-адÑеÑов.
diff --git a/app/src/main/assets/ru/guide_ssl_certificates_light.html b/app/src/main/assets/ru/guide_ssl_certificates_light.html
index 618860e2..e566b4eb 100644
--- a/app/src/main/assets/ru/guide_ssl_certificates_light.html
+++ b/app/src/main/assets/ru/guide_ssl_certificates_light.html
@@ -40,8 +40,8 @@
СÑок дейÑÑÐ²Ð¸Ñ ÑеÑÑиÑикаÑов SSL иÑÑÐµÐºÐ°ÐµÑ Ð² ÑказаннÑÑ Ð´Ð°ÑÑ, поÑÑÐ¾Ð¼Ñ Ð´Ð°Ð¶Ðµ закÑепленнÑе ÑеÑÑиÑикаÑÑ SSL бÑдÑÑ Ð¿ÐµÑиодиÑеÑки обновлÑÑÑÑÑ.
Ðак пÑавило, закÑепление ÑеÑÑиÑикаÑов SSL в болÑÑинÑÑве ÑлÑÑаев не ÑÑебÑеÑÑÑ.
- Ðо Ð´Ð»Ñ ÑеÑ
, кÑо подозÑеваеÑ, ÑÑо за ними ведеÑÑÑ Ð½Ð°Ð±Ð»Ñдение, закÑепление ÑеÑÑиÑикаÑа SSL Ð¼Ð¾Ð¶ÐµÑ Ð¾Ð±Ð½Ð°ÑÑжиÑÑ Ð¸ помеÑаÑÑ Ð°Ñаке MITM.
- Privacy Browser also has the ability to pin IP addresses.
+ Ðо Ð´Ð»Ñ Ñого, кÑо подозÑеваеÑ, ÑÑо за ним ведеÑÑÑ Ð½Ð°Ð±Ð»Ñдение, закÑепление ÑеÑÑиÑикаÑа SSL Ð¿Ð¾Ð¼Ð¾Ð¶ÐµÑ Ð¾Ð±Ð½Ð°ÑÑжиÑÑ Ð¸ помеÑаÑÑ Ð°Ñаке MITM.
+ Privacy Browser Ñакже Ð¸Ð¼ÐµÐµÑ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ÑÑÑ Ð·Ð°ÐºÑÐµÐ¿Ð»ÐµÐ½Ð¸Ñ IP-адÑеÑов.
diff --git a/app/src/main/assets/shared_images/file_copy_dark.png b/app/src/main/assets/shared_images/file_copy_dark.png
new file mode 100644
index 0000000000000000000000000000000000000000..b0aa33e20a9a339c5bb63af8364df9ef708c8812
GIT binary patch
literal 1220
zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7uRSEX7WqAsj$Z!;#Vf4nJ
z`0WK@#^S6D5ul)CiEBhjaDG}zd16s2LwR|*US?i)adKios$PCk`s{Z$Qb2PvM=$xyGBdFFMN{`D&|Q_I-APdE@pAp8xmDCfsD)@w@o|
z>w|wUk4@aURx74-(fq|q|xw5#G#ODfdy;893~AvMv>(VUBL`TOKpWM*vsviWSIMy%-Q-k
zPxnsSCT)9UR}kBVU*{jcKA?Tzu+Nt-pW15PzFY3zt8iEKz}{qI0n;$u5
z;LNbN=KKSp4|$KXFS$=pY^Yw$ZlPgp&AjLCk4;yqn|K&y`s5b0&aCzL?KPpyAEz=k>au{rs#ngjjlu2joB#I8Aj4cGOqo~>778i{
zFbGTQGO=!8RCWalKQwQwmQ)3rt#ge-P@{qOv|J<849902A_sgnxh-Hwn_>u(`=pm(
ze^cp%fP>{sr2q!UO+fRRQacH;@Y43mzaw>M4pvdr+@tZ+7i}z2ze=>1J<^6I=S(0H4oJ+vbquKDth-Ja<1_w!|
z2@bHx({pCH#lm(f5X}rmTswOvCjjhL(|6&9>$jU&RVCDP3Kww6n&wmfMNkqg;^tt
z>A-XabXxdgc8ME#?-`9)K6rm&VE(=HJA)+C551~}#?t?9fr6Pp!QB_C8$S3yyz^J!
z19O4<7oFyxycT(BvDGU+h#ojo*7$w#bBPNx?@i|C3}av@zt3dHJg;wFs(^T7`q?+{
z?=dgsU^sA>pLu#?`=WHuchV15UOjbF_m9vAedFJ<6+Ayws-_*WW12VP)f4AM3=C)9
zteUx(!-ly=+SXqwydgO4+p3v=4WBk%j$!)t=-u(g%Lfb}XcRcVaoPM+_{Y@ja`*UI
kH~Tj}?f`-li~cZ2*a`)&iFkVsSmH2vy85}Sb4q9e0I>@AE&u=k
literal 0
HcmV?d00001
diff --git a/app/src/main/assets/shared_images/file_copy_light.png b/app/src/main/assets/shared_images/file_copy_light.png
new file mode 100644
index 0000000000000000000000000000000000000000..2b8a6849a56945277f44a1994a1094a5228a6b14
GIT binary patch
literal 1121
zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7uRSEX7WqAsj$Z!;#Vf4nJ
z`0WK@#^S6D5ul)CiEBhjaDG}zd16s2LwR|*US?i)adKios$PCk`s{Z$QVa~t_dH!3
zLn`LHy?c7KZ=%HUkNWJ2B0HZL70#9n>`FS&(l{mP9}{z`2OFC^o6$%669TF`m+?#t
zIu)Tgea5LHOC{dKuswfp_x-KcKli@=S(|nC%~Q+QHRo@=zjdwXz!@O8H1qPsx@8S_
z55y(tZrHW>@3vP$A5^M3>~vWCnB^PZ->b`Y&*saT|15M*nREg3jXM{0#1if>d}G?)
zuvGS3663SJkF$CYnEp8{-%`l5gJ;dFDuM1msRN1$=T+D?N-SlRnSVk=Y?(^K(F6ZH
zd6Nvim~L?C%(|i=wpgX1RCxBGSp`!V>x8D=wV2*$rog9Q$v$BYv&T6`mFEnTiWyE?
zHawAWC^FsN@-@|T17`%Y4#&JyweOAn7oB^&O@T&oK9JZj_uad=c`e5eFiQ13sQ>yk
zpV7U+oV9-Xp>G@!VINJ~1EZPeq?YV|`0Iru*ITA{7ZX^#80WB@TU@*A|LoWU&Iy%Y
z6jVc)Zk&9PIqTY6hP=xcW;&=gaKF&=@HQ<~3*bml-=JUB(Pvr=WIrhQ!tj6P7o+u^
ztqYwP*4m^O$XkT}kow6X!gNEx)}b-;fzQM2CD&QF7}s>jEx6!%>O9M)Y^GKQku(j{
zvkvE2bmeM9#WnjA*gr5VM|~Jz&^%HHL+6
zLA9^z0|vFlGl9lS98xM!^L1GuaA3kocc7rZ6%+df{+mEU>Q#V-{GHSdG-P2M>lrsK
zpnXP@Iu9@&nb-j&xcY$vckcnlj1@aCZs~GjxVerg&G>AE`K+)&jstbl&)+WSV!m)W
zn}yFII3H;L1JeU%syY)i&OiD7@cI9VCK_Dn4BSs(aUsI=A=E*kl|g3$ERKRX4s;)I
z;9`tWfJNMLfd=u$MiHhB4oI;H6kN~5q{))d2#ij!c+APkj_Vs&v({geTCjoXKbwy$
z|HTcf-Y>k*1dVqQrhtg{B})YwSc_ok0I0E*p+;1|LlMB^nW?A0oC6
zhwZMt1BOfaMP`#Xxo`PIm?{K;f~)%HIx(0pX3x0MdrMZMhHE=d1?QR`xh0134Z0!g
z7K+R}@sp>9mCuF$(v08(i(e@2GGe`x_$7Mhp1;+*yN~hbFArll-)X<&)$L%n{MpT0
zSa$gRaSXe=j-mUQ-7a2B4jbiv4Xbnd8*4+|OT@Us8S6K)6EGBMThjL45;c{=0
z-Kb6bea`uQzOT>c`{(y+aG;Ma29E&%==%9m
zPG~Y`Gf+qkcM+rdGzp$c_M@US$VNpMXnHiu_jD=%fzLMsk{Ta}(+sz!d4;BhB-7I}
zqEcc&Mn;A$BZ--MK8h7%o179`sr0}DfY{8>w-T>hgr1_v4yE~QZA|*qBYQhv&Iox%!pw&@i^_bHP?@Z{AQ+S
zljyj=&E)ppC;Myt2HaxriqvXCesp7*+%9WTwCJhME%*}edTv?EmCjHL@%J=?R%GZT
zwS&mQ8y{#6IuY6Xp%XT(WOBpBSn;FTRrbSFbCfPeG=-KUPWp`WOHR}HH`(Q@WHWo9
zy!`DFQMltiy~oV~dj&3VRs;z)KE9Zxpls3io3(Y<&o#pJTGyUi*;T7_q~nSlo>R8}
z^|ajuKS8_)r$l2NY+T7FL1u%ZG{V(UAZ$u9L8_tj*mban7E(ie&LwbYbbXtMD5P+!%
zxIl>I+3p9qo^Q#~3`xU?aXj+t!mnQVe_L=jORf9b;@)A?#w|e3g{27X8?^_fyOpJ||WpFZ%hTP?QXbK!)WI)OoM^
zI%iaSmP~}{zw-2HbS%}sb6I>$`Z)b)v4hx^)-CC=C~8~dQBALA#6-*5<_Ewt1P%Y1
zL0@z{J|TMwTa?}_=^Zp;-K(#}X9I_WI@aCyGwpLV1zvt}y01x}4%Fqa?_x#jRP@>E|Ga)r=3DG7WsHY97RaSJ%M8dSR#
z5EHUCC2*Ed(LR`hBApklBV2wIW)zq}ml&Ose5^{Z^av>4E&s&soHS2CiJS61PwFs=
zZ9Dj&Hv4_g7IYTB*Bmghs)&luMwWl)r1Kv6<~||db=H|pqYH+JoQS43M({<1C7AGD
ze|z(srLhxKy>nJ{t(#|R&IiDwQZS>9&dl%I1>9wyxKD09TeD}-t5~jb$ojwAgfAom
zPGjA&Q;@qbUV!Sfr2?|vj5c0K9KgR(_Hmxh_D=isS^-c3vvV@cjp(o7@T-~i<$oEm
z+C9Z($NRVb#^?w@CD%gPJ}w)zQ}Jc&Dp3bA
z8!@q+eIdMb!N`8xR6I`g@6w_>%`C4dFFUdh*<(C(8sZd~ieI$Vh+~kxg%L(6tnBQ{
zIyiN_YChaQN2W(iq*shu85nY~)EpPaR0;j$f{WWZeU^tH-zGrO!KMIma@P&7vB;;S
zZX==L$_+i)f$)N@_v?RW_Cp`X0ta{Rbl)8c`~&wDgOj3ZJ@{JGZ)}{eMWdAprxCDA6Gy^oj
za=b=tCv=vqg<}$8k4nJ
z`0WK@#^S6D5ul)CiEBhjaDG}zd16s2LwR|*US?i)adKios$PCk`s{Z$Qb2>EJY5_^
zD(1Ysd%im?ROa}{?-mQYIfYd&TvR#DdCE0~r7I*UU|W#J_kV(IU;MXpsc;@?^-yDR
zb=#?_+bzbrc9CIk;F@0Uqw+S7`!fq;d^n81@BCi)#n3fA>7teYyDEcboZ^-z(qQ
zeCO*x119Fod-!YEW7ymdI6c_K_vXc>1w_l0PV&n(E&ieZLSS{*<%2v`hp#V_i(&Xxm&HzTWOzKy|ap;hV{+To5sE2Ous$+A5;V!
z_^2QLZjX4v%3q8(t}Wa?z40^amxT;Hk)ID#-?;PLfKi6&o!EuxOcxg9G3KYg_{@80
z@twWDgBhl+5d4sKEbjiIJ^TBgoU3LOyWY5GEmOgTmFBDoGp#1>zUXRq{zf~~g+=R_
zUl*D0I>Ga@cUk%Uxy%8lryl5UsIQ)rFJ^J+agFQef1Eq`bI#m*y4cKi&h6d*{jzQ_
z?oR){w6Y_%?5-2LV>$UmF@
zEtsFpl-<)h`F<_mewLqUW
zq34(AjjRjz!yasB`W46!b*1)!%FA>23v(IlmhF*t6loAEZ7;ch!1}-~)v8GDJ=`_F
zKK!qcFaO36u_sdM;+t)+gLD|C|NQ@%aUO#iv;V{5$en8|6GEpm>dcy}tUKq+`~0AS
z=*>TJAE-7PE}45SJ72J2J@2szWq#$d2Of!RXf2T}yOf}l`A+VGjBGh$x9kI^f-?y+
zlII1vtp2WJU-#($L=CUAjQ(a-sa0EKcl7>$bR};4i*FBBuZdGydbxUH)>`cY+P(3-
z>aQF9HU0~|n(#`mU-qV2(Ck8mE_vy edit.
expand_less.
expand_more.
+ file_copy.
file_download.
find_in_page.
folder.
@@ -130,6 +131,7 @@
new releases.
question_answer.
refresh.
+ save.
search.
select_all.
settings.
diff --git a/app/src/main/assets/tr/about_licenses_light.html b/app/src/main/assets/tr/about_licenses_light.html
index 8fd7c674..5ecac0a9 100644
--- a/app/src/main/assets/tr/about_licenses_light.html
+++ b/app/src/main/assets/tr/about_licenses_light.html
@@ -111,6 +111,7 @@
edit.
expand_less.
expand_more.
+ file_copy.
file_download.
find_in_page.
folder.
@@ -131,6 +132,7 @@
new releases.
question_answer.
refresh.
+ save.
search.
select_all.
settings.
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 a2d17975..80a3a98f 100644
--- a/app/src/main/java/com/stoutner/privacybrowser/activities/ImportExportActivity.java
+++ b/app/src/main/java/com/stoutner/privacybrowser/activities/ImportExportActivity.java
@@ -21,9 +21,9 @@ package com.stoutner.privacybrowser.activities;
import android.Manifest;
import android.app.Activity;
-import android.app.DialogFragment;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@@ -33,6 +33,7 @@ import android.support.annotation.NonNull;
import android.support.design.widget.Snackbar;
import android.support.design.widget.TextInputLayout;
import android.support.v4.app.ActivityCompat;
+import android.support.v4.app.DialogFragment;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.FileProvider;
import android.support.v7.app.ActionBar;
@@ -53,7 +54,7 @@ import android.widget.Spinner;
import android.widget.TextView;
import com.stoutner.privacybrowser.R;
-import com.stoutner.privacybrowser.dialogs.ImportExportStoragePermissionDialog;
+import com.stoutner.privacybrowser.dialogs.StoragePermissionDialog;
import com.stoutner.privacybrowser.helpers.ImportExportDatabaseHelper;
import java.io.File;
@@ -70,7 +71,7 @@ import javax.crypto.CipherOutputStream;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
-public class ImportExportActivity extends AppCompatActivity implements ImportExportStoragePermissionDialog.ImportExportStoragePermissionDialogListener {
+public class ImportExportActivity extends AppCompatActivity implements StoragePermissionDialog.StoragePermissionDialogListener {
// Create the encryption constants.
private final int NO_ENCRYPTION = 0;
private final int PASSWORD_ENCRYPTION = 1;
@@ -81,7 +82,7 @@ public class ImportExportActivity extends AppCompatActivity implements ImportExp
private final int OPENPGP_EXPORT_RESULT_CODE = 1;
// `openKeychainInstalled` is accessed from an inner class.
- boolean openKeychainInstalled;
+ private boolean openKeychainInstalled;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -161,11 +162,11 @@ public class ImportExportActivity extends AppCompatActivity implements ImportExp
// Set the default file paths according to the storage permission status.
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { // The storage permission has been granted.
// Set the default file paths to use the external public directory.
- defaultFilePath = Environment.getExternalStorageDirectory() + "/" + getString(R.string.privacy_browser_settings);
+ defaultFilePath = Environment.getExternalStorageDirectory() + "/" + getString(R.string.privacy_browser_settings_pbs);
defaultPasswordEncryptionFilePath = defaultFilePath + ".aes";
} else { // The storage permission has not been granted.
// Set the default file paths to use the external private directory.
- defaultFilePath = getApplicationContext().getExternalFilesDir(null) + "/" + getString(R.string.privacy_browser_settings);
+ defaultFilePath = getApplicationContext().getExternalFilesDir(null) + "/" + getString(R.string.privacy_browser_settings_pbs);
defaultPasswordEncryptionFilePath = defaultFilePath + ".aes";
}
@@ -342,7 +343,7 @@ public class ImportExportActivity extends AppCompatActivity implements ImportExp
}
});
- // Hide the storage permissions TextView on API < 23 as permissions on older devices are automatically granted.
+ // Hide the storage permissions text view on API < 23 as permissions on older devices are automatically granted.
if (Build.VERSION.SDK_INT < 23) {
storagePermissionTextView.setVisibility(View.GONE);
}
@@ -416,15 +417,15 @@ public class ImportExportActivity extends AppCompatActivity implements ImportExp
// Create the file picker intent.
Intent importBrowseIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
- // Set the intent MIME type to include all files.
+ // Set the intent MIME type to include all files so that everything is visible.
importBrowseIntent.setType("*/*");
- // Set the initial directory if API >= 26.
+ // Set the initial directory if the minimum API >= 26.
if (Build.VERSION.SDK_INT >= 26) {
importBrowseIntent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, Environment.getExternalStorageDirectory());
}
- // Specify that a file that can be opened is requested.
+ // Request a file that can be opened.
importBrowseIntent.addCategory(Intent.CATEGORY_OPENABLE);
// Launch the file picker.
@@ -433,18 +434,18 @@ public class ImportExportActivity extends AppCompatActivity implements ImportExp
// Create the file picker intent.
Intent exportBrowseIntent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
- // Set the intent MIME type to include all files.
+ // Set the intent MIME type to include all files so that everything is visible.
exportBrowseIntent.setType("*/*");
// Set the initial export file name.
- exportBrowseIntent.putExtra(Intent.EXTRA_TITLE, getString(R.string.privacy_browser_settings));
+ exportBrowseIntent.putExtra(Intent.EXTRA_TITLE, getString(R.string.privacy_browser_settings_pbs));
- // Set the initial directory if API >= 26.
+ // Set the initial directory if the minimum API >= 26.
if (Build.VERSION.SDK_INT >= 26) {
exportBrowseIntent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, Environment.getExternalStorageDirectory());
}
- // Specify that a file that can be opened is requested.
+ // Request a file that can be opened.
exportBrowseIntent.addCategory(Intent.CATEGORY_OPENABLE);
// Launch the file picker.
@@ -479,9 +480,9 @@ public class ImportExportActivity extends AppCompatActivity implements ImportExp
String fileNameString = fileNameEditText.getText().toString();
// Get the external private directory `File`.
- File externalPrivateDirectoryFile = getApplicationContext().getExternalFilesDir(null);
+ File externalPrivateDirectoryFile = getExternalFilesDir(null);
- // Remove the lint error below that the `File` might be null.
+ // Remove the incorrect lint error below that the file might be null.
assert externalPrivateDirectoryFile != null;
// Get the external private directory string.
@@ -501,10 +502,10 @@ public class ImportExportActivity extends AppCompatActivity implements ImportExp
// 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 importExportStoragePermissionDialogFragment = new ImportExportStoragePermissionDialog();
+ DialogFragment storagePermissionDialogFragment = new StoragePermissionDialog();
// Show the storage permission alert dialog. The permission will be requested when the dialog is closed.
- importExportStoragePermissionDialogFragment.show(getFragmentManager(), getString(R.string.storage_permission));
+ storagePermissionDialogFragment.show(getSupportFragmentManager(), getString(R.string.storage_permission));
} else { // Show the permission request directly.
// Request the storage permission. The export will be run when it finishes.
ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0);
@@ -514,7 +515,7 @@ public class ImportExportActivity extends AppCompatActivity implements ImportExp
}
@Override
- public void onCloseImportExportStoragePermissionDialog() {
+ public void onCloseStoragePermissionDialog() {
// 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);
}
@@ -524,25 +525,19 @@ public class ImportExportActivity extends AppCompatActivity implements ImportExp
// Get a handle for the import radiobutton.
RadioButton importRadioButton = findViewById(R.id.import_radiobutton);
- // Check to see if import or export is selected.
- if (importRadioButton.isChecked()) { // Import is selected.
- // 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.
+ // 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.
+ // Run the import or export methods according to which radio button is selected.
+ if (importRadioButton.isChecked()) { // Import is selected.
// Import the settings.
importSettings();
- } else { // The storage permission was not granted.
- // Display an error snackbar.
- Snackbar.make(importRadioButton, getString(R.string.cannot_import), Snackbar.LENGTH_LONG).show();
- }
- } else { // Export is selected.
- // 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.
+ } else { // Export is selected.
// Export the settings.
exportSettings();
- } else { // The storage permission was not granted.
- // Display an error snackbar.
- Snackbar.make(importRadioButton, getString(R.string.cannot_export), Snackbar.LENGTH_LONG).show();
}
+ } else { // The storage permission was not granted.
+ // Display an error snackbar.
+ Snackbar.make(importRadioButton, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show();
}
}
@@ -552,7 +547,7 @@ public class ImportExportActivity extends AppCompatActivity implements ImportExp
case (BROWSE_RESULT_CODE):
// Don't do anything if the user pressed back from the file picker.
if (resultCode == Activity.RESULT_OK) {
- // Get a handle for the file name EditText.
+ // Get a handle for the file name edit text.
EditText fileNameEditText = findViewById(R.id.file_name_edittext);
// Get the file name URI.
@@ -564,7 +559,7 @@ public class ImportExportActivity extends AppCompatActivity implements ImportExp
// Get the raw file name path.
String rawFileNamePath = fileNameUri.getPath();
- // Remove the warning that the file name path might be null.
+ // Remove the incorrect lint warning that the file name path might be null.
assert rawFileNamePath != null;
// Check to see if the file name Path includes a valid storage location.
@@ -604,7 +599,7 @@ public class ImportExportActivity extends AppCompatActivity implements ImportExp
case OPENPGP_EXPORT_RESULT_CODE:
// Get the temporary unencrypted export file.
- File temporaryUnencryptedExportFile = new File(getApplicationContext().getCacheDir() + "/" + getString(R.string.privacy_browser_settings));
+ File temporaryUnencryptedExportFile = new File(getApplicationContext().getCacheDir() + "/" + getString(R.string.privacy_browser_settings_pbs));
// Delete the temporary unencrypted export file if it exists.
if (temporaryUnencryptedExportFile.exists()) {
@@ -623,11 +618,14 @@ public class ImportExportActivity extends AppCompatActivity implements ImportExp
// Instantiate the import export database helper.
ImportExportDatabaseHelper importExportDatabaseHelper = new ImportExportDatabaseHelper();
+ // Get the export file string.
+ String exportFileString = fileNameEditText.getText().toString();
+
// Get the export and temporary unencrypted export files.
- File exportFile = new File(fileNameEditText.getText().toString());
- File temporaryUnencryptedExportFile = new File(getApplicationContext().getCacheDir() + "/" + getString(R.string.privacy_browser_settings));
+ File exportFile = new File(exportFileString);
+ File temporaryUnencryptedExportFile = new File(getApplicationContext().getCacheDir() + "/" + getString(R.string.privacy_browser_settings_pbs));
- // Initialize the export status string.
+ // Create an export status string.
String exportStatus;
// Export according to the encryption type.
@@ -776,6 +774,9 @@ public class ImportExportActivity extends AppCompatActivity implements ImportExp
startActivityForResult(openKeychainEncryptIntent, OPENPGP_EXPORT_RESULT_CODE);
break;
}
+
+ // Add the file to the list of recent files. This doesn't currently work, but maybe it will someday.
+ MediaScannerConnection.scanFile(this, new String[] {exportFileString}, new String[] {"application/x-sqlite3"}, null);
}
private void importSettings() {
@@ -801,7 +802,7 @@ public class ImportExportActivity extends AppCompatActivity implements ImportExp
case PASSWORD_ENCRYPTION:
// Use a private temporary import location.
- File temporaryUnencryptedImportFile = new File(getApplicationContext().getCacheDir() + "/" + getString(R.string.privacy_browser_settings));
+ File temporaryUnencryptedImportFile = new File(getApplicationContext().getCacheDir() + "/" + getString(R.string.privacy_browser_settings_pbs));
try {
// Create an encrypted import file input stream.
diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/LogcatActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/LogcatActivity.java
new file mode 100644
index 00000000..32d1bf47
--- /dev/null
+++ b/app/src/main/java/com/stoutner/privacybrowser/activities/LogcatActivity.java
@@ -0,0 +1,448 @@
+/*
+ * Copyright © 2019 Soren Stoutner .
+ *
+ * This file is part of 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 .
+ */
+
+package com.stoutner.privacybrowser.activities;
+
+import android.Manifest;
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.media.MediaScannerConnection;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Environment;
+import android.support.annotation.NonNull;
+import android.support.design.widget.Snackbar;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.app.DialogFragment;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.widget.SwipeRefreshLayout;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.app.AppCompatDialogFragment;
+import android.support.v7.widget.Toolbar;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.WindowManager;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import com.stoutner.privacybrowser.R;
+import com.stoutner.privacybrowser.dialogs.StoragePermissionDialog;
+import com.stoutner.privacybrowser.dialogs.SaveLogcatDialog;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.lang.ref.WeakReference;
+import java.nio.charset.StandardCharsets;
+
+public class LogcatActivity extends AppCompatActivity implements SaveLogcatDialog.SaveLogcatListener, StoragePermissionDialog.StoragePermissionDialogListener {
+ private String filePathString;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ // Disable screenshots if not allowed.
+ if (!MainWebViewActivity.allowScreenshots) {
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
+ }
+
+ // Set the activity theme.
+ if (MainWebViewActivity.darkTheme) {
+ setTheme(R.style.PrivacyBrowserDark_SecondaryActivity);
+ } else {
+ setTheme(R.style.PrivacyBrowserLight_SecondaryActivity);
+ }
+
+ // Run the default commands.
+ super.onCreate(savedInstanceState);
+
+ // Set the content view.
+ setContentView(R.layout.logcat_coordinatorlayout);
+
+ // Use the `SupportActionBar` from `android.support.v7.app.ActionBar` until the minimum API is >= 21.
+ Toolbar logcatAppBar = findViewById(R.id.logcat_toolbar);
+ setSupportActionBar(logcatAppBar);
+
+ // Get a handle for the app bar.
+ ActionBar appBar = getSupportActionBar();
+
+ // Remove the incorrect lint warning that `appBar` might be null.
+ assert appBar != null;
+
+ // Display the the back arrow in the app bar.
+ appBar.setDisplayHomeAsUpEnabled(true);
+
+ // Implement swipe to refresh.
+ SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.logcat_swiperefreshlayout);
+ swipeRefreshLayout.setOnRefreshListener(() -> {
+ // Get the current logcat.
+ new GetLogcat(this).execute();
+ });
+
+ // Set the swipe to refresh color according to the theme.
+ if (MainWebViewActivity.darkTheme) {
+ swipeRefreshLayout.setColorSchemeResources(R.color.blue_600);
+ swipeRefreshLayout.setProgressBackgroundColorSchemeResource(R.color.gray_800);
+ } else {
+ swipeRefreshLayout.setColorSchemeResources(R.color.blue_700);
+ }
+
+ // Get the logcat.
+ new GetLogcat(this).execute();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu. This adds items to the action bar.
+ getMenuInflater().inflate(R.menu.logcat_options_menu, menu);
+
+ // Display the menu.
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem menuItem) {
+ // Get the selected menu item ID.
+ int menuItemId = menuItem.getItemId();
+
+ // Run the commands that correlate to the selected menu item.
+ switch (menuItemId) {
+ case R.id.copy:
+ // Get a handle for the clipboard manager.
+ ClipboardManager clipboardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
+
+ // Get a handle for the logcat text view.
+ TextView logcatTextView = findViewById(R.id.logcat_textview);
+
+ // Save the logcat in a ClipData.
+ ClipData logcatClipData = ClipData.newPlainText(getString(R.string.logcat), logcatTextView.getText());
+
+ // Remove the incorrect lint error that `clipboardManager.setPrimaryClip()` might produce a null pointer exception.
+ assert clipboardManager != null;
+
+ // Place the ClipData on the clipboard.
+ clipboardManager.setPrimaryClip(logcatClipData);
+
+ // Display a snackbar.
+ Snackbar.make(logcatTextView, R.string.logcat_copied, Snackbar.LENGTH_SHORT).show();
+
+ // Consume the event.
+ return true;
+
+ case R.id.save:
+ // Get a handle for the save alert dialog.
+ DialogFragment saveDialogFragment = new SaveLogcatDialog();
+
+ // Show the save alert dialog.
+ saveDialogFragment.show(getSupportFragmentManager(), getString(R.string.save_logcat));
+
+ // Consume the event.
+ return true;
+
+ case R.id.clear:
+ try {
+ // Clear the logcat. `-c` clears the logcat. `-b all` clears all the buffers (instead of just crash, main, and system).
+ Process process = Runtime.getRuntime().exec("logcat -b all -c");
+
+ // Wait for the process to finish.
+ process.waitFor();
+
+ // Reload the logcat.
+ new GetLogcat(this).execute();
+ } catch (IOException|InterruptedException exception) {
+ // Do nothing.
+ }
+
+ // Consume the event.
+ return true;
+
+ default:
+ // Don't consume the event.
+ return super.onOptionsItemSelected(menuItem);
+ }
+ }
+
+ @Override
+ public void onSaveLogcat(AppCompatDialogFragment dialogFragment) {
+ // Get a handle for the file name edit text.
+ EditText fileNameEditText = dialogFragment.getDialog().findViewById(R.id.file_name_edittext);
+
+ // Get the file path string.
+ filePathString = 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 logcat.
+ saveLogcat(filePathString);
+ } else { // The storage permission has not been granted.
+ // Get the external private directory `File`.
+ File externalPrivateDirectoryFile = getExternalFilesDir(null);
+
+ // Remove the incorrect lint error below that the file might be null.
+ assert externalPrivateDirectoryFile != null;
+
+ // Get the external private directory string.
+ String externalPrivateDirectory = externalPrivateDirectoryFile.toString();
+
+ // Check to see if the file path is in the external private directory.
+ if (filePathString.startsWith(externalPrivateDirectory)) { // The file path is in the external private directory.
+ // Save the logcat.
+ saveLogcat(filePathString);
+ } else { // The file path in 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();
+
+ // 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 storage permission. The logcat will be saved when it finishes.
+ ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0);
+
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onCloseStoragePermissionDialog() {
+ // 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);
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ // Check to see if the storage permission was granted. If the dialog was canceled the grant result will be empty.
+ if ((grantResults.length > 0) && (grantResults[0] == PackageManager.PERMISSION_GRANTED)) { // The storage permission was granted.
+ // Save the logcat.
+ saveLogcat(filePathString);
+ } else { // The storage permission was not granted.
+ // Get a handle for the logcat text view.
+ TextView logcatTextView = findViewById(R.id.logcat_textview);
+
+ // Display an error snackbar.
+ Snackbar.make(logcatTextView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show();
+ }
+ }
+
+ private void saveLogcat(String fileNameString) {
+ // Get a handle for the logcat text view.
+ TextView logcatTextView = findViewById(R.id.logcat_textview);
+
+ try {
+ // Get the logcat as a string.
+ String logcatString = logcatTextView.getText().toString();
+
+ // Create an input stream with the contents of the logcat.
+ InputStream logcatInputStream = new ByteArrayInputStream(logcatString.getBytes(StandardCharsets.UTF_8));
+
+ // Create a logcat buffered reader.
+ BufferedReader logcatBufferedReader = new BufferedReader(new InputStreamReader(logcatInputStream));
+
+ // Create a file from the file name string.
+ File saveFile = new File(fileNameString);
+
+ // Create a file buffered writer.
+ BufferedWriter fileBufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(saveFile)));
+
+ // Create a transfer string.
+ String transferString;
+
+ // Use the transfer string to copy the logcat from the buffered reader to the buffered writer.
+ while ((transferString = logcatBufferedReader.readLine()) != null) {
+ // Append the line to the buffered writer.
+ fileBufferedWriter.append(transferString);
+
+ // Append a line break.
+ fileBufferedWriter.append("\n");
+ }
+
+ // Close the buffered reader and writer.
+ logcatBufferedReader.close();
+ fileBufferedWriter.close();
+
+ // Add the file to the list of recent files. This doesn't currently work, but maybe it will someday.
+ MediaScannerConnection.scanFile(this, new String[] {fileNameString}, new String[] {"text/plain"}, null);
+
+ // Display a snackbar.
+ Snackbar.make(logcatTextView, getString(R.string.file_saved_successfully), Snackbar.LENGTH_SHORT).show();
+ } catch (Exception exception) {
+ // Display a snackbar with the error message.
+ Snackbar.make(logcatTextView, getString(R.string.save_failed) + " " + exception.toString(), Snackbar.LENGTH_INDEFINITE).show();
+ }
+ }
+
+ // The activity result is called after browsing for a file in the save alert dialog.
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ // 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 saveDialogFragment = (DialogFragment) getSupportFragmentManager().findFragmentByTag(getString(R.string.save_logcat));
+
+ // Remove the incorrect lint error that the save dialog fragment might be null.
+ assert saveDialogFragment != null;
+
+ // Get a handle for the save dialog.
+ Dialog saveDialog = saveDialogFragment.getDialog();
+
+ // Get a handle for the file name edit text.
+ EditText fileNameEditText = saveDialog.findViewById(R.id.file_name_edittext);
+
+ // Get the file name URI.
+ Uri fileNameUri = data.getData();
+
+ // Remove the incorrect lint warning that the file name URI might be null.
+ assert fileNameUri != null;
+
+ // Get the raw file name path.
+ String rawFileNamePath = fileNameUri.getPath();
+
+ // Remove the incorrect lint warning that the file name path might be null.
+ assert rawFileNamePath != null;
+
+ // Check to see if the file name Path includes a valid storage location.
+ if (rawFileNamePath.contains(":")) { // The path is valid.
+ // Split the path into the initial content uri and the final path information.
+ String fileNameContentPath = rawFileNamePath.substring(0, rawFileNamePath.indexOf(":"));
+ String fileNameFinalPath = rawFileNamePath.substring(rawFileNamePath.indexOf(":") + 1);
+
+ // Create the file name path string.
+ String fileNamePath;
+
+ // Construct the file name path.
+ switch (fileNameContentPath) {
+ // The documents home has a special content path.
+ case "/document/home":
+ fileNamePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS) + "/" + fileNameFinalPath;
+ break;
+
+ // Everything else for the primary user should be in `/document/primary`.
+ case "/document/primary":
+ fileNamePath = Environment.getExternalStorageDirectory() + "/" + fileNameFinalPath;
+ break;
+
+ // Just in case, catch everything else and place it in the external storage directory.
+ default:
+ fileNamePath = Environment.getExternalStorageDirectory() + "/" + fileNameFinalPath;
+ break;
+ }
+
+ // Set the file name path as the text of the file name edit text.
+ fileNameEditText.setText(fileNamePath);
+ } else { // The path is invalid.
+ // Close the alert dialog.
+ saveDialog.dismiss();
+
+ // Get a handle for the logcat text view.
+ TextView logcatTextView = findViewById(R.id.logcat_textview);
+
+ // Display a snackbar with the error message.
+ Snackbar.make(logcatTextView, rawFileNamePath + " " + getString(R.string.invalid_location), Snackbar.LENGTH_INDEFINITE).show();
+ }
+ }
+ }
+
+ // `Void` does not declare any parameters. `Void` does not declare progress units. `String` contains the results.
+ private static class GetLogcat extends AsyncTask {
+ // Create a weak reference to the calling activity.
+ private final WeakReference activityWeakReference;
+
+ // Populate the weak reference to the calling activity.
+ GetLogcat(Activity activity) {
+ activityWeakReference = new WeakReference<>(activity);
+ }
+
+ @Override
+ protected String doInBackground(Void... parameters) {
+ // Get a handle for the activity.
+ Activity activity = activityWeakReference.get();
+
+ // Abort if the activity is gone.
+ if ((activity == null) || activity.isFinishing()) {
+ return "";
+ }
+
+ // Create a log string builder.
+ StringBuilder logStringBuilder = new StringBuilder();
+
+ try {
+ // Get the logcat. `-b all` gets all the buffers (instead of just crash, main, and system). `-v long` produces more complete information. `-d` dumps the logcat and exits.
+ Process process = Runtime.getRuntime().exec("logcat -b all -v long -d");
+
+ // Wrap the logcat in a buffered reader.
+ BufferedReader logBufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
+
+ // Create a log transfer string.
+ String logTransferString;
+
+ // Use the log transfer string to copy the logcat from the buffered reader to the string builder.
+ while ((logTransferString = logBufferedReader.readLine()) != null) {
+ // Append a line.
+ logStringBuilder.append(logTransferString);
+
+ // Append a line break.
+ logStringBuilder.append("\n");
+ }
+
+ // Close the buffered reader.
+ logBufferedReader.close();
+ } catch (IOException exception) {
+ // Do nothing.
+ }
+
+ // Return the logcat.
+ return logStringBuilder.toString();
+ }
+
+ // `onPostExecute()` operates on the UI thread.
+ @Override
+ protected void onPostExecute(String logcatString) {
+ // Get a handle for the activity.
+ Activity activity = activityWeakReference.get();
+
+ // Abort if the activity is gone.
+ if ((activity == null) || activity.isFinishing()) {
+ return;
+ }
+
+ // Get handles for the views.
+ TextView logcatTextView = activity.findViewById(R.id.logcat_textview);
+ SwipeRefreshLayout swipeRefreshLayout = activity.findViewById(R.id.logcat_swiperefreshlayout);
+
+ // Display the logcat.
+ logcatTextView.setText(logcatString);
+
+ // Stop the swipe to refresh animation if it is displayed.
+ swipeRefreshLayout.setRefreshing(false);
+ }
+ }
+}
\ No newline at end of file
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 65528ebf..868b6d71 100644
--- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java
+++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java
@@ -2046,7 +2046,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
@Override
public boolean onCreateOptionsMenu(Menu menu) {
- // Inflate the menu; this adds items to the action bar if it is present.
+ // Inflate the menu. This adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.webview_options_menu, menu);
// Set mainMenu so it can be used by `onOptionsItemSelected()` and `updatePrivacyIcons`.
@@ -2312,7 +2312,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
// Get the selected menu item ID.
int menuItemId = menuItem.getItemId();
- // Set the commands that relate to the menu entries.
+ // Run the commands that correlate to the selected menu item.
switch (menuItemId) {
case R.id.toggle_javascript:
// Switch the status of javaScriptEnabled.
@@ -2532,15 +2532,22 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
// Setup a runnable to manually delete the DOM storage files and directories.
Runnable deleteDomStorageRunnable = () -> {
try {
- // A `String[]` must be used because the directory contains a space and `Runtime.exec` will otherwise not escape the string correctly.
- privacyBrowserRuntime.exec(new String[]{"rm", "-rf", privateDataDirectoryString + "/app_webview/Local Storage/"});
+ // A string array must be used because the directory contains a space and `Runtime.exec` will otherwise not escape the string correctly.
+ Process deleteLocalStorageProcess = privacyBrowserRuntime.exec(new String[]{"rm", "-rf", privateDataDirectoryString + "/app_webview/Local Storage/"});
// Multiple commands must be used because `Runtime.exec()` does not like `*`.
- privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/IndexedDB");
- privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager");
- privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager-journal");
- privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/databases");
- } catch (IOException e) {
+ Process deleteIndexProcess = privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/IndexedDB");
+ Process deleteQuotaManagerProcess = privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager");
+ Process deleteQuotaManagerJournalProcess = privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager-journal");
+ Process deleteDatabasesProcess = privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/databases");
+
+ // Wait for the processes to finish.
+ deleteLocalStorageProcess.waitFor();
+ deleteIndexProcess.waitFor();
+ deleteQuotaManagerProcess.waitFor();
+ deleteQuotaManagerJournalProcess.waitFor();
+ deleteDatabasesProcess.waitFor();
+ } catch (Exception exception) {
// Do nothing if an error is thrown.
}
};
@@ -3016,6 +3023,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
startActivity(importExportIntent);
break;
+ case R.id.logcat:
+ // Launch the logcat activity.
+ Intent logcatIntent = new Intent(this, LogcatActivity.class);
+ startActivity(logcatIntent);
+ break;
+
case R.id.guide:
// Launch `GuideActivity`.
Intent guideIntent = new Intent(this, GuideActivity.class);
@@ -3028,14 +3041,15 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
startActivity(aboutIntent);
break;
- case R.id.clearAndExit:
+ case R.id.clear_and_exit:
// Close the bookmarks cursor and database.
bookmarksCursor.close();
bookmarksDatabaseHelper.close();
- // Get a handle for `sharedPreferences`. `this` references the current context.
+ // Get a handle for the shared preferences.
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+ // Get the status of the clear everything preference.
boolean clearEverything = sharedPreferences.getBoolean("clear_everything", true);
// Clear cookies.
@@ -3049,10 +3063,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
// Manually delete the cookies database, as `CookieManager` sometimes will not flush its changes to disk before `System.exit(0)` is run.
try {
- // We have to use two commands because `Runtime.exec()` does not like `*`.
- privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/Cookies");
- privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/Cookies-journal");
- } catch (IOException e) {
+ // Two commands must be used because `Runtime.exec()` does not like `*`.
+ Process deleteCookiesProcess = privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/Cookies");
+ Process deleteCookiesJournalProcess = privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/Cookies-journal");
+
+ // Wait until the processes have finished.
+ deleteCookiesProcess.waitFor();
+ deleteCookiesJournalProcess.waitFor();
+ } catch (Exception exception) {
// Do nothing if an error is thrown.
}
}
@@ -3066,14 +3084,21 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
// Manually delete the DOM storage files and directories, as `WebStorage` sometimes will not flush its changes to disk before `System.exit(0)` is run.
try {
// A `String[]` must be used because the directory contains a space and `Runtime.exec` will otherwise not escape the string correctly.
- privacyBrowserRuntime.exec(new String[] {"rm", "-rf", privateDataDirectoryString + "/app_webview/Local Storage/"});
+ Process deleteLocalStorageProcess = privacyBrowserRuntime.exec(new String[] {"rm", "-rf", privateDataDirectoryString + "/app_webview/Local Storage/"});
// Multiple commands must be used because `Runtime.exec()` does not like `*`.
- privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/IndexedDB");
- privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager");
- privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager-journal");
- privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/databases");
- } catch (IOException e) {
+ Process deleteIndexProcess = privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/IndexedDB");
+ Process deleteQuotaManagerProcess = privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager");
+ Process deleteQuotaManagerJournalProcess = privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager-journal");
+ Process deleteDatabaseProcess = privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/databases");
+
+ // Wait until the processes have finished.
+ deleteLocalStorageProcess.waitFor();
+ deleteIndexProcess.waitFor();
+ deleteQuotaManagerProcess.waitFor();
+ deleteQuotaManagerJournalProcess.waitFor();
+ deleteDatabaseProcess.waitFor();
+ } catch (Exception exception) {
// Do nothing if an error is thrown.
}
}
@@ -3085,10 +3110,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
// Manually delete the form data database, as `WebViewDatabase` sometimes will not flush its changes to disk before `System.exit(0)` is run.
try {
- // We have to use a `String[]` because the database contains a space and `Runtime.exec` will not escape the string correctly otherwise.
- privacyBrowserRuntime.exec(new String[] {"rm", "-f", privateDataDirectoryString + "/app_webview/Web Data"});
- privacyBrowserRuntime.exec(new String[] {"rm", "-f", privateDataDirectoryString + "/app_webview/Web Data-journal"});
- } catch (IOException e) {
+ // A string array must be used because the database contains a space and `Runtime.exec` will not otherwise escape the string correctly.
+ Process deleteWebDataProcess = privacyBrowserRuntime.exec(new String[] {"rm", "-f", privateDataDirectoryString + "/app_webview/Web Data"});
+ Process deleteWebDataJournalProcess = privacyBrowserRuntime.exec(new String[] {"rm", "-f", privateDataDirectoryString + "/app_webview/Web Data-journal"});
+
+ // Wait until the processes have finished.
+ deleteWebDataProcess.waitFor();
+ deleteWebDataJournalProcess.waitFor();
+ } catch (Exception exception) {
// Do nothing if an error is thrown.
}
}
@@ -3101,12 +3130,16 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
// Manually delete the cache directories.
try {
// Delete the main cache directory.
- privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/cache");
+ Process deleteCacheProcess = privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/cache");
// Delete the secondary `Service Worker` cache directory.
- // A `String[]` must be used because the directory contains a space and `Runtime.exec` will not escape the string correctly otherwise.
- privacyBrowserRuntime.exec(new String[] {"rm", "-rf", privateDataDirectoryString + "/app_webview/Service Worker/"});
- } catch (IOException e) {
+ // A string array must be used because the directory contains a space and `Runtime.exec` will otherwise not escape the string correctly.
+ Process deleteServiceWorkerProcess = privacyBrowserRuntime.exec(new String[] {"rm", "-rf", privateDataDirectoryString + "/app_webview/Service Worker/"});
+
+ // Wait until the processes have finished.
+ deleteCacheProcess.waitFor();
+ deleteServiceWorkerProcess.waitFor();
+ } catch (Exception exception) {
// Do nothing if an error is thrown.
}
}
@@ -3133,8 +3166,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
// See `https://code.google.com/p/android/issues/detail?id=233826&thanks=233826&ts=1486670530`.
if (clearEverything) {
try {
- privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview");
- } catch (IOException e) {
+ // Delete the folder.
+ Process deleteAppWebviewProcess = privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview");
+
+ // Wait until the process has finished.
+ deleteAppWebviewProcess.waitFor();
+ } catch (Exception exception) {
// Do nothing if an error is thrown.
}
}
diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/RequestsActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/RequestsActivity.java
index 71ce3070..098fd66f 100644
--- a/app/src/main/java/com/stoutner/privacybrowser/activities/RequestsActivity.java
+++ b/app/src/main/java/com/stoutner/privacybrowser/activities/RequestsActivity.java
@@ -137,7 +137,7 @@ public class RequestsActivity extends AppCompatActivity implements ViewRequestDi
spinnerCursor.addRow(new Object[]{4, getString(R.string.blocked_plural) + " - " + blockedResourceRequests.size()});
// Create a resource cursor adapter for the spinner.
- ResourceCursorAdapter spinnerCursorAdapter = new ResourceCursorAdapter(this, R.layout.appbar_spinner_item, spinnerCursor, 0) {
+ ResourceCursorAdapter spinnerCursorAdapter = new ResourceCursorAdapter(this, R.layout.requests_appbar_spinner_item, spinnerCursor, 0) {
@Override
public void bindView(View view, Context context, Cursor cursor) {
// Get a handle for the spinner item text view.
@@ -149,7 +149,7 @@ public class RequestsActivity extends AppCompatActivity implements ViewRequestDi
};
// Set the resource cursor adapter drop down view resource.
- spinnerCursorAdapter.setDropDownViewResource(R.layout.appbar_spinner_dropdown_item);
+ spinnerCursorAdapter.setDropDownViewResource(R.layout.requests_appbar_spinner_dropdown_item);
// Get a handle for the app bar spinner and set the adapter.
Spinner appBarSpinner = findViewById(R.id.spinner);
diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/ViewSourceActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/ViewSourceActivity.java
index 8e7f5e96..62c704de 100644
--- a/app/src/main/java/com/stoutner/privacybrowser/activities/ViewSourceActivity.java
+++ b/app/src/main/java/com/stoutner/privacybrowser/activities/ViewSourceActivity.java
@@ -206,7 +206,7 @@ public class ViewSourceActivity extends AppCompatActivity {
@Override
public boolean onCreateOptionsMenu(Menu menu) {
- // Inflate the menu; this adds items to the action bar if it is present.
+ // 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.
@@ -291,7 +291,7 @@ public class ViewSourceActivity extends AppCompatActivity {
}
}
- // `String` declares the parameters. `Void` does not declare progress units. `String[]` contains the results.
+ // `String` declares the parameters. `Void` does not declare progress units. `SpannableStringBuilder[]` contains the results.
private static class GetSource extends AsyncTask {
// Create a weak reference to the calling activity.
private WeakReference activityWeakReference;
@@ -663,7 +663,7 @@ public class ViewSourceActivity extends AppCompatActivity {
// `onPostExecute()` operates on the UI thread.
@Override
protected void onPostExecute(SpannableStringBuilder[] viewSourceStringArray){
- // Get a handle the activity.
+ // Get a handle for the activity.
Activity activity = activityWeakReference.get();
// Abort if the activity is gone.
diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDialog.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDialog.java
index 0bb5f9da..64efa611 100644
--- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDialog.java
+++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDialog.java
@@ -116,27 +116,27 @@ public class EditBookmarkDialog extends AppCompatDialogFragment {
// Set the title.
dialogBuilder.setTitle(R.string.edit_bookmark);
- // Remove the incorrect lint warning that `getActivity()` might be null.
+ // Remove the incorrect lint warning that `getActivity().getLayoutInflater()` might be null.
assert getActivity() != null;
// Set the view. The parent view is null because it will be assigned by the alert dialog.
dialogBuilder.setView(getActivity().getLayoutInflater().inflate(R.layout.edit_bookmark_dialog, null));
- // Set the listener for the negative button.
+ // Set the cancel button listener.
dialogBuilder.setNegativeButton(R.string.cancel, (DialogInterface dialog, int which) -> {
- // Do nothing. The `AlertDialog` will close automatically.
+ // Do nothing. The alert dialog will close automatically.
});
- // Set the listener fo the positive button.
+ // Set the save button listener.
dialogBuilder.setPositiveButton(R.string.save, (DialogInterface dialog, int which) -> {
- // Return the `DialogFragment` to the parent activity on save.
+ // Return the dialog fragment to the parent activity.
editBookmarkListener.onSaveBookmark(EditBookmarkDialog.this, selectedBookmarkDatabaseId);
});
- // Create an alert dialog from the alert dialog builder.
+ // Create an alert dialog from the builder.
final AlertDialog alertDialog = dialogBuilder.create();
- // Remove the warning below that `getWindow()` might be null.
+ // remove the incorrect lint warning below that `getWindow().addFlags()` might be null.
assert alertDialog.getWindow() != null;
// Disable screenshots if not allowed.
@@ -175,7 +175,7 @@ public class EditBookmarkDialog extends AppCompatDialogFragment {
currentName = bookmarkCursor.getString(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME));
currentUrl = bookmarkCursor.getString(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_URL));
- // Populate the `EditTexts`.
+ // Populate the edit texts.
nameEditText.setText(currentName);
urlEditText.setText(currentUrl);
@@ -226,7 +226,7 @@ public class EditBookmarkDialog extends AppCompatDialogFragment {
}
});
- // Allow the `enter` key on the keyboard to save the bookmark from the bookmark name `EditText`.
+ // Allow the enter key on the keyboard to save the bookmark from the bookmark name edit text.
nameEditText.setOnKeyListener((View v, int keyCode, KeyEvent event) -> {
// Save the bookmark if the event is a key-down on the "enter" button.
if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER) && editButton.isEnabled()) { // The enter key was pressed and the edit button is enabled.
@@ -243,14 +243,14 @@ public class EditBookmarkDialog extends AppCompatDialogFragment {
}
});
- // Allow the "enter" key on the keyboard to save the bookmark from the URL `EditText`.
+ // Allow the enter key on the keyboard to save the bookmark from the URL edit text.
urlEditText.setOnKeyListener((View v, int keyCode, KeyEvent event) -> {
// Save the bookmark if the event is a key-down on the "enter" button.
if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER) && editButton.isEnabled()) { // The enter key was pressed and the edit button is enabled.
// Trigger the `Listener` and return the DialogFragment to the parent activity.
editBookmarkListener.onSaveBookmark(EditBookmarkDialog.this, selectedBookmarkDatabaseId);
- // Manually dismiss the `AlertDialog`.
+ // Manually dismiss the alert dialog.
alertDialog.dismiss();
// Consume the event.
@@ -260,7 +260,7 @@ public class EditBookmarkDialog extends AppCompatDialogFragment {
}
});
- // `onCreateDialog` requires the return of an `AlertDialog`.
+ // Return the alert dialog.
return alertDialog;
}
diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/PinnedMismatchDialog.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/PinnedMismatchDialog.java
index 2d506dc4..0a25771d 100644
--- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/PinnedMismatchDialog.java
+++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/PinnedMismatchDialog.java
@@ -126,14 +126,15 @@ public class PinnedMismatchDialog extends AppCompatDialogFragment {
pinnedSslCertificate = getArguments().getBoolean("Pinned_SSL_Certificate");
pinnedIpAddresses = getArguments().getBoolean("Pinned_IP_Addresses");
- if (MainWebViewActivity.favoriteIconBitmap.equals(MainWebViewActivity.favoriteIconDefaultBitmap)) {
+ // Set the favorite icon as the dialog icon if it exists.
+ if (MainWebViewActivity.favoriteIconBitmap.equals(MainWebViewActivity.favoriteIconDefaultBitmap)) { // There is no favorite icon.
// Set the icon according to the theme.
if (MainWebViewActivity.darkTheme) {
dialogBuilder.setIcon(R.drawable.ssl_certificate_enabled_dark);
} else {
dialogBuilder.setIcon(R.drawable.ssl_certificate_enabled_light);
}
- } else {
+ } else { // There is a favorite icon.
// Create a drawable version of the favorite icon.
Drawable favoriteIconDrawable = new BitmapDrawable(getResources(), MainWebViewActivity.favoriteIconBitmap);
diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/SaveLogcatDialog.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/SaveLogcatDialog.java
new file mode 100644
index 00000000..4faafcba
--- /dev/null
+++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/SaveLogcatDialog.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright © 2016-2019 Soren Stoutner .
+ *
+ * This file is part of 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 .
+ */
+
+package com.stoutner.privacybrowser.dialogs;
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Environment;
+import android.provider.DocumentsContract;
+import android.support.annotation.NonNull;
+import android.support.v4.content.ContextCompat;
+// `AppCompatDialogFragment` is required instead of `DialogFragment` or an error is produced on API <=22. It is also required for the browser button to work correctly.
+import android.support.v7.app.AppCompatDialogFragment;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import com.stoutner.privacybrowser.R;
+import com.stoutner.privacybrowser.activities.MainWebViewActivity;
+
+public class SaveLogcatDialog extends AppCompatDialogFragment {
+ // Instantiate the class variables.
+ private SaveLogcatListener saveLogcatListener;
+ private Context parentContext;
+
+ // The public interface is used to send information back to the parent activity.
+ public interface SaveLogcatListener {
+ void onSaveLogcat(AppCompatDialogFragment dialogFragment);
+ }
+
+ public void onAttach(Context context) {
+ // Run the default commands.
+ super.onAttach(context);
+
+ // Store a handle for the context.
+ parentContext = context;
+
+ // Get a handle for `SaveLogcatListener` from the launching context.
+ saveLogcatListener = (SaveLogcatListener) context;
+ }
+
+ // `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`.
+ @SuppressLint("InflateParams")
+ @Override
+ @NonNull
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ // Use an alert dialog builder to create the alert dialog.
+ AlertDialog.Builder dialogBuilder;
+
+ // Set the style according to the theme.
+ if (MainWebViewActivity.darkTheme) {
+ dialogBuilder = new AlertDialog.Builder(getActivity(), R.style.PrivacyBrowserAlertDialogDark);
+ } else {
+ dialogBuilder = new AlertDialog.Builder(getActivity(), R.style.PrivacyBrowserAlertDialogLight);
+ }
+
+ // Set the title.
+ dialogBuilder.setTitle(R.string.save_logcat);
+
+ // Remove the incorrect lint warning that `getActivity().getLayoutInflater()` might be null.
+ assert getActivity() != null;
+
+ // Set the view. The parent view is null because it will be assigned by the alert dialog.
+ dialogBuilder.setView(getActivity().getLayoutInflater().inflate(R.layout.save_logcat_dialog, null));
+
+ // Set the icon according to the theme.
+ if (MainWebViewActivity.darkTheme) {
+ dialogBuilder.setIcon(R.drawable.save_dialog_dark);
+ } else {
+ dialogBuilder.setIcon(R.drawable.save_dialog_light);
+ }
+
+ // Set the cancel button listener.
+ dialogBuilder.setNegativeButton(R.string.cancel, (DialogInterface dialog, int which) -> {
+ // Do nothing. The alert dialog will close automatically.
+ });
+
+ // Set the save button listener.
+ dialogBuilder.setPositiveButton(R.string.save, (DialogInterface dialog, int which) -> {
+ // Return the dialog fragment to the parent activity.
+ saveLogcatListener.onSaveLogcat(this);
+ });
+
+ // Create an alert dialog from the builder.
+ AlertDialog alertDialog = dialogBuilder.create();
+
+ // Remove the incorrect lint warning below that `getWindow().addFlags()` might be null.
+ assert alertDialog.getWindow() != null;
+
+ // Disable screenshots if not allowed.
+ if (!MainWebViewActivity.allowScreenshots) {
+ alertDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
+ }
+
+ // The alert dialog must be shown before items in the layout can be modified.
+ alertDialog.show();
+
+ // Get handles for the layout items.
+ EditText fileNameEditText = alertDialog.findViewById(R.id.file_name_edittext);
+ Button browseButton = alertDialog.findViewById(R.id.browse_button);
+ TextView storagePermissionTextView = alertDialog.findViewById(R.id.storage_permission_textview);
+ Button saveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
+
+ // Create a string for the default file path.
+ String defaultFilePath;
+
+ // Set the default file path according to the storage permission state.
+ if (ContextCompat.checkSelfPermission(parentContext, 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.privacy_browser_logcat_txt);
+ } else { // The storage permission has not been granted.
+ // Set the default file path to use the external private directory.
+ defaultFilePath = parentContext.getExternalFilesDir(null) + "/" + getString(R.string.privacy_browser_logcat_txt);
+ }
+
+ // Display the default file path.
+ fileNameEditText.setText(defaultFilePath);
+
+ // Update the status of the save button when the file name changes.
+ fileNameEditText.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ // Do nothing.
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ // Do nothing.
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ // Enable the save button if a file name exists.
+ saveButton.setEnabled(!fileNameEditText.getText().toString().isEmpty());
+ }
+ });
+
+ // Handle clicks on the browse button.
+ browseButton.setOnClickListener((View view) -> {
+ // Create the file picker intent.
+ Intent browseIntent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
+
+ // 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.privacy_browser_logcat_txt));
+
+ // Set the initial directory if the minimum API >= 26.
+ if (Build.VERSION.SDK_INT >= 26) {
+ browseIntent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, Environment.getExternalStorageDirectory());
+ }
+
+ // Request a file that can be opened.
+ browseIntent.addCategory(Intent.CATEGORY_OPENABLE);
+
+ // Launch the file picker. There is only one `startActivityForResult()`, so the request code is simply set to 0.
+ startActivityForResult(browseIntent, 0);
+ });
+
+ // Hide the storage permission text view on API < 23 as permissions on older devices are automatically granted.
+ if (Build.VERSION.SDK_INT < 23) {
+ storagePermissionTextView.setVisibility(View.GONE);
+ }
+
+ // Return the alert dialog.
+ return alertDialog;
+ }
+}
diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/ImportExportStoragePermissionDialog.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/StoragePermissionDialog.java
similarity index 82%
rename from app/src/main/java/com/stoutner/privacybrowser/dialogs/ImportExportStoragePermissionDialog.java
rename to app/src/main/java/com/stoutner/privacybrowser/dialogs/StoragePermissionDialog.java
index 447b67e6..ed119595 100644
--- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/ImportExportStoragePermissionDialog.java
+++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/StoragePermissionDialog.java
@@ -21,22 +21,24 @@ package com.stoutner.privacybrowser.dialogs;
import android.app.AlertDialog;
import android.app.Dialog;
-import android.app.DialogFragment;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
+// `AppCompatDialogFragment` must be used instead of `DialogFragment` or the browse button doesn't work correctly in the other dialog for saving logcats.
+import android.support.annotation.NonNull;
+import android.support.v7.app.AppCompatDialogFragment;
import android.view.WindowManager;
import com.stoutner.privacybrowser.R;
import com.stoutner.privacybrowser.activities.MainWebViewActivity;
-public class ImportExportStoragePermissionDialog extends DialogFragment {
+public class StoragePermissionDialog extends AppCompatDialogFragment {
// The listener is used in `onAttach()` and `onCreateDialog()`.
- private ImportExportStoragePermissionDialogListener importExportStoragePermissionDialogListener;
+ private StoragePermissionDialogListener storagePermissionDialogListener;
// The public interface is used to send information back to the parent activity.
- public interface ImportExportStoragePermissionDialogListener {
- void onCloseImportExportStoragePermissionDialog();
+ public interface StoragePermissionDialogListener {
+ void onCloseStoragePermissionDialog();
}
@Override
@@ -45,9 +47,10 @@ public class ImportExportStoragePermissionDialog extends DialogFragment {
super.onAttach(context);
// Get a handle for the listener from the launching context.
- importExportStoragePermissionDialogListener = (ImportExportStoragePermissionDialogListener) context;
+ storagePermissionDialogListener = (StoragePermissionDialogListener) context;
}
+ @NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Use a builder to create the alert dialog.
@@ -71,7 +74,7 @@ public class ImportExportStoragePermissionDialog extends DialogFragment {
// Set an `onClick` listener on the negative button.
dialogBuilder.setNegativeButton(R.string.ok, (DialogInterface dialog, int which) -> {
// Inform the parent activity that the dialog was closed.
- importExportStoragePermissionDialogListener.onCloseImportExportStoragePermissionDialog();
+ storagePermissionDialogListener.onCloseStoragePermissionDialog();
});
// Create an alert dialog from the builder.
diff --git a/app/src/main/res/drawable/bug.xml b/app/src/main/res/drawable/bug.xml
new file mode 100644
index 00000000..c21c2eb6
--- /dev/null
+++ b/app/src/main/res/drawable/bug.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/clear_dark.xml b/app/src/main/res/drawable/clear_dark.xml
new file mode 100644
index 00000000..738b53fd
--- /dev/null
+++ b/app/src/main/res/drawable/clear_dark.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/clear_light.xml b/app/src/main/res/drawable/clear_light.xml
new file mode 100644
index 00000000..a30170d5
--- /dev/null
+++ b/app/src/main/res/drawable/clear_light.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/cookies_warning.xml b/app/src/main/res/drawable/cookies_warning.xml
index 733448f6..6f497590 100644
--- a/app/src/main/res/drawable/cookies_warning.xml
+++ b/app/src/main/res/drawable/cookies_warning.xml
@@ -11,7 +11,7 @@
android:autoMirrored="true"
tools:ignore="VectorRaster" >
-
+
diff --git a/app/src/main/res/drawable/copy_dark.xml b/app/src/main/res/drawable/copy_dark.xml
new file mode 100644
index 00000000..f37115ab
--- /dev/null
+++ b/app/src/main/res/drawable/copy_dark.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/copy_light.xml b/app/src/main/res/drawable/copy_light.xml
new file mode 100644
index 00000000..1c2dc993
--- /dev/null
+++ b/app/src/main/res/drawable/copy_light.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/save_dark.xml b/app/src/main/res/drawable/save_dark.xml
new file mode 100644
index 00000000..e26d3bfd
--- /dev/null
+++ b/app/src/main/res/drawable/save_dark.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/save_dialog_dark.xml b/app/src/main/res/drawable/save_dialog_dark.xml
new file mode 100644
index 00000000..f49b8abf
--- /dev/null
+++ b/app/src/main/res/drawable/save_dialog_dark.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/save_dialog_light.xml b/app/src/main/res/drawable/save_dialog_light.xml
new file mode 100644
index 00000000..e4592c84
--- /dev/null
+++ b/app/src/main/res/drawable/save_dialog_light.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/save_light.xml b/app/src/main/res/drawable/save_light.xml
new file mode 100644
index 00000000..df119f5c
--- /dev/null
+++ b/app/src/main/res/drawable/save_light.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/select_all_dark.xml b/app/src/main/res/drawable/select_all_dark.xml
index 6f3fad01..61008a29 100644
--- a/app/src/main/res/drawable/select_all_dark.xml
+++ b/app/src/main/res/drawable/select_all_dark.xml
@@ -6,8 +6,8 @@
android:viewportHeight="24.0"
android:viewportWidth="24.0" >
-
+
-
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/select_all_light.xml b/app/src/main/res/drawable/select_all_light.xml
index 590eca08..e5ed2b13 100644
--- a/app/src/main/res/drawable/select_all_light.xml
+++ b/app/src/main/res/drawable/select_all_light.xml
@@ -6,8 +6,8 @@
android:viewportHeight="24.0"
android:viewportWidth="24.0" >
-
+
-
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ssl_certificate_enabled_dark.xml b/app/src/main/res/drawable/ssl_certificate_enabled_dark.xml
index d0ff3a30..9d5436c1 100644
--- a/app/src/main/res/drawable/ssl_certificate_enabled_dark.xml
+++ b/app/src/main/res/drawable/ssl_certificate_enabled_dark.xml
@@ -11,8 +11,8 @@
android:autoMirrored="true"
tools:ignore="VectorRaster" >
-
+
-
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ssl_certificate_enabled_light.xml b/app/src/main/res/drawable/ssl_certificate_enabled_light.xml
index 42407ce7..80d5541d 100644
--- a/app/src/main/res/drawable/ssl_certificate_enabled_light.xml
+++ b/app/src/main/res/drawable/ssl_certificate_enabled_light.xml
@@ -11,8 +11,8 @@
android:autoMirrored="true"
tools:ignore="VectorRaster" >
-
+
-
+
\ No newline at end of file
diff --git a/app/src/main/res/layout-w900dp/bookmarks_drawer.xml b/app/src/main/res/layout-w900dp/bookmarks_drawer.xml
index e5e9d919..2b7073ef 100644
--- a/app/src/main/res/layout-w900dp/bookmarks_drawer.xml
+++ b/app/src/main/res/layout-w900dp/bookmarks_drawer.xml
@@ -1,22 +1,22 @@
+ You should have received a copy of the GNU General Public License
+ along with Privacy Browser. If not, see . -->
. -->
android:id="@+id/bookmarks_title_textview"
android:layout_height="wrap_content"
android:layout_width="match_parent"
- android:paddingTop="35dp"
- android:paddingBottom="8dp"
- android:paddingStart="15dp"
- android:paddingEnd="35dp"
android:textStyle="bold"
android:textSize="20sp"
android:background="?attr/navigationHeaderBackground"
diff --git a/app/src/main/res/layout-w900dp/domains_list_fragment.xml b/app/src/main/res/layout-w900dp/domains_list_fragment.xml
index 2dbb6980..43d01599 100644
--- a/app/src/main/res/layout-w900dp/domains_list_fragment.xml
+++ b/app/src/main/res/layout-w900dp/domains_list_fragment.xml
@@ -1,6 +1,6 @@
-
+
+ You should have received a copy of the GNU General Public License
+ along with Privacy Browser. If not, see . -->
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/navigation_header.xml b/app/src/main/res/layout/navigation_header.xml
index fe46e20e..3eb496d1 100644
--- a/app/src/main/res/layout/navigation_header.xml
+++ b/app/src/main/res/layout/navigation_header.xml
@@ -1,7 +1,7 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/requests_appbar_spinner_item.xml b/app/src/main/res/layout/requests_appbar_spinner_item.xml
new file mode 100644
index 00000000..459f8547
--- /dev/null
+++ b/app/src/main/res/layout/requests_appbar_spinner_item.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/requests_coordinatorlayout.xml b/app/src/main/res/layout/requests_coordinatorlayout.xml
index 126fd5fa..510c0a36 100644
--- a/app/src/main/res/layout/requests_coordinatorlayout.xml
+++ b/app/src/main/res/layout/requests_coordinatorlayout.xml
@@ -1,7 +1,7 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/logcat_options_menu.xml b/app/src/main/res/menu/logcat_options_menu.xml
new file mode 100644
index 00000000..e2d1a609
--- /dev/null
+++ b/app/src/main/res/menu/logcat_options_menu.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/webview_navigation_menu.xml b/app/src/main/res/menu/webview_navigation_menu.xml
index ba07e1a1..74a9249c 100644
--- a/app/src/main/res/menu/webview_navigation_menu.xml
+++ b/app/src/main/res/menu/webview_navigation_menu.xml
@@ -1,7 +1,7 @@
@@ -89,22 +95,22 @@
android:id="@+id/guide"
android:title="@string/guide"
android:icon="@drawable/guide"
- android:orderInCategory="100" />
+ android:orderInCategory="110" />
+ android:orderInCategory="120" />
+ android:orderInCategory="130" />
\ No newline at end of file
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 33cdd2e6..f9a7e66b 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -1,7 +1,7 @@
Privacy Browser
- Privacy Browser Einstellungen
de
@@ -257,8 +256,10 @@
SSL-Zertifikat der aktuellen Webseite
Zuerst verschlüsselte Webseite laden...
+
+ Privacy Browser Einstellungen.pbs
+
- Privacy Browser Handbuch
Ãbersicht
Lokale Speicherung
SSL-Zertifikate
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 3392ebc3..f4d57e32 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -23,7 +23,6 @@
Navegador Privado
- Configuración de Navegador Privado
es
@@ -327,6 +326,7 @@
El cifrado de contraseñas no funciona en Android KitKat.
El cifrado OpenPGP requiere que esté instalado OpenKeychain.
El archivo sin cifrar tendrá que ser importado en un paso separado después de ser descifrado.
+ Configuración de Navegador Privado.pbs
Ubicación del archivo
Navegar
Exportar
@@ -335,8 +335,6 @@
Exportación exitosa.
Exportación fallida:
Importación fallida:
- Los ajustes no pueden exportarse a esta ubicación porque no se ha concedido el permiso de almacenamiento.
- Los ajustes no pueden importarse desde esta ubicación porque no se ha concedido el permiso de almacenamiento.
no es una ubicación válida.
Permiso de almacenamiento
Navegador Privado necesita el permiso de almacenamiento para acceder a los directorios públicos.
@@ -345,7 +343,6 @@
De lo contrario, sólo funcionarán los directorios de aplicaciones.
- GuÃa de Navegador Privado
Visión general
Almacenamiento local
Certificados SSL
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index edc20f92..5c6f51c7 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -23,7 +23,6 @@
Privacy Browser
- Impostazioni
it
@@ -334,8 +333,6 @@
Esportazione riuscita
Esportazione fallita:
Importazione fallita:
- Le impostazioni non possono essere esportate in questa cartella perché non si è in possesso dei permessi di accesso alla memoria.
- Le impostazioni non possono essere importate in questa cartella perché non si è in possesso dei permessi di accesso alla memoria.
non è una cartella valida.
Permesso di accesso alla memoria
Privacy Browser necessita del permesso di accesso alla memoria per poter accedere alle cartelle pubbliche.
@@ -344,7 +341,6 @@
altrimenti saranno accessibili solo le cartelle dell\'applicazione.
- Guida di Privacy Browser
Descrizione
Archiviazione Locale
Certificati SSL
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 8a147b56..0ae9442a 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -21,7 +21,6 @@
Privacy Browser
- ÐаÑÑÑойки Privacy Browser
ru -->
@@ -323,6 +322,7 @@
ШиÑÑование паÑолем не ÑабоÑÐ°ÐµÑ Ð½Ð° Android KitKat.
ÐÐ»Ñ Ð¸ÑполÑÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ ÑиÑÑÐ¾Ð²Ð°Ð½Ð¸Ñ OpenPGP необÑ
одимо пÑиложение OpenKeychain.
ÐезаÑиÑÑованнÑй Ñайл должен бÑÑÑ Ð¸Ð¼Ð¿Ð¾ÑÑиÑован на оÑделÑном Ñаге поÑле его деÑиÑÑованиÑ.
+ ÐаÑÑÑойки Privacy Browser.pbs
РаÑположение Ñайла
ÐбзоÑ
ÐкÑпоÑÑ
@@ -331,15 +331,12 @@
ÐкÑпоÑÑ Ð²Ñполнен.
Сбой пÑи ÑкÑпоÑÑе:
Сбой пÑи импоÑÑе:
- ÐаÑÑÑойки не могÑÑ Ð±ÑÑÑ ÑкÑпоÑÑиÑÐ¾Ð²Ð°Ð½Ñ Ð² ÑÑо ÑаÑположение, Ñак как не бÑло пÑедоÑÑавлено ÑазÑеÑение на доÑÑÑп к Ñ
ÑанилиÑÑ.
- ÐаÑÑÑойки не могÑÑ Ð±ÑÑÑ Ð¸Ð¼Ð¿Ð¾ÑÑиÑÐ¾Ð²Ð°Ð½Ñ Ð¸Ð· ÑÑого ÑаÑположениÑ, Ñак как не бÑло пÑедоÑÑавлено ÑазÑеÑение на доÑÑÑп к Ñ
ÑанилиÑÑ.
- недопÑÑÑимое ÑаÑположение.
ÐоÑÑÑп к Ñ
ÑанилиÑÑ
Privacy Browser необÑ
одимо ÑазÑеÑение на доÑÑÑп к внеÑним папкам. ÐÑли доÑÑÑп пÑедоÑÑавлен не бÑдеÑ, можно иÑполÑзоваÑÑ Ð»Ð¾ÐºÐ°Ð»ÑнÑÑ Ð¿Ð°Ð¿ÐºÑ Ð¿ÑиложениÑ.
ÐÐ»Ñ Ð´Ð¾ÑÑÑпа к Ñайлам во внеÑниÑ
папкаÑ
ÑÑебÑеÑÑÑ ÑооÑвеÑÑÑвÑÑÑее ÑазÑеÑение. РпÑоÑивном ÑлÑÑае бÑдÑÑ ÑабоÑаÑÑ ÑолÑко локалÑнÑе папки.
- Ð ÑководÑÑво по Privacy Browser
ÐбзоÑ
ÐокалÑное Ñ
ÑанилиÑе
СеÑÑиÑикаÑÑ SSL
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index 02384336..2e3a4685 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -21,7 +21,6 @@
Privacy Browser
- Privacy Browser Ayarları
tr
@@ -322,6 +321,7 @@
Android KitKat sürümünde parola Åifrelemesi çalıÅmaz.
OpenPGP Åifrelemesinin çalıÅması için OpenKeychain yüklü olmalıdır.
Åifresi çözüldükten sonra, ÅifrelenmemiÅ dosya ayrı bir adımda içeri aktarılmak zorundadır.
+ Privacy Browser Ayarları.pbs
Dosya Konumu
Gözat
DıÅarı aktar
@@ -330,15 +330,12 @@
DıÅa aktarım baÅarılı.
DıÅa aktarım baÅarısız oldu:
İçe aktarım baÅarısız oldu:
- Ayarlar, depolama alanı izni onaylanmadıÄından bu konuma aktarılamıyor.
- Ayarlar, depolama alanı izni onaylanmadıÄından bu konumdan aktarılamıyor.
geçerli bir konum deÄildir.
Depolama Alanı İzni
Privacy Browser, genel dizinlere eriÅmek için depolama alanı iznine ihtiyaç duymaktadır. ReddedildiÄi takdirde, uygulamanın dizinleri hala kullanılabilir.
Genel dizinlerdeki dosyalara eriÅim icin depolama alanı izni gerekmektedir. Aksi takdirde, sadece uygulamanın dizinleri çalıÅacaktır.
- Privacy Browser Rehberi
Genel BakıÅ
Yerel Depolama Alanı
SSL Sertifikaları
diff --git a/app/src/main/res/values-v21/styles.xml b/app/src/main/res/values-v21/styles.xml
index 54a16ce0..a22b10c3 100644
--- a/app/src/main/res/values-v21/styles.xml
+++ b/app/src/main/res/values-v21/styles.xml
@@ -66,9 +66,12 @@
- @drawable/add_light
- @drawable/create_bookmark_light
- @drawable/create_folder_light
+ - @drawable/copy_light
+ - @drawable/clear_light
- @drawable/select_all_light
- @drawable/edit_light
- @drawable/move_to_folder_light
+ - @drawable/save_light
- @drawable/sort_light
- @style/PrivacyBrowserPopupsLight
- @style/PrivacyBrowserAppBarWhiteText
@@ -162,9 +165,12 @@
- @drawable/add_dark
- @drawable/create_bookmark_dark
- @drawable/create_folder_dark
+ - @drawable/clear_dark
+ - @drawable/copy_dark
- @drawable/select_all_dark
- @drawable/edit_dark
- @drawable/move_to_folder_dark
+ - @drawable/save_dark
- @drawable/sort_dark
- @style/PrivacyBrowserAppBarDark
- @style/PrivacyBrowserTabLayoutDark
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
index 62e251c2..c673ba65 100644
--- a/app/src/main/res/values/attrs.xml
+++ b/app/src/main/res/values/attrs.xml
@@ -51,17 +51,20 @@
-
-
-
-
-
-
+
-
+
+
+
+
+
+
-
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 8618c220..e14b85f6 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -28,7 +28,6 @@
Privacy Browser
- Privacy Browser Settings
en
@@ -120,6 +119,7 @@
Downloads
Settings
Import/Export
+ Logcat
Guide
About
Clear and Exit
@@ -331,6 +331,7 @@
Password encryption does not work on Android KitKat.
OpenPGP encryption requires that OpenKeychain be installed.
The unencrypted file will have to be imported in a separate step after it is decrypted.
+ Privacy Browser Settings.pbs
File Location
Browse
Export
@@ -339,15 +340,22 @@
Export successful.
Export failed:
Import failed:
- The settings cannot be exported to this location because the storage permission has not been granted.
- The settings cannot be imported from this location because the storage permission has not been granted.
is not a valid location.
Storage Permission
Privacy Browser needs the storage permission to access public directories. If it is denied, the appâs directories can still be used.
Accessing files in public directories requires the storage permission. Otherwise, only app directories will work.
+ This location cannot be used because the storage permission has not been granted.
+
+
+ Copy
+ Logcat copied.
+ Clear
+ Save logcat
+ Privacy Browser Logcat.txt
+ File saved successfully.
+ Save failed:
- Privacy Browser Guide
Overview
Local Storage
SSL Certificates
@@ -463,14 +471,14 @@
- WebView default user agent
- Mozilla/5.0 (Android 9; Mobile; rv:63.0) Gecko/63.0 Firefox/63.0
- Mozilla/5.0 (Linux; Android 9; Pixel 2 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Mobile Safari/537.36
- - Mozilla/5.0 (iPhone; CPU iPhone OS 12_0_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1
+ - Mozilla/5.0 (iPhone; CPU iPhone OS 12_1_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1
- Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0
- Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36
- Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0
- Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36
- Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17134
- Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko
- - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.1 Safari/605.1.15
+ - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0.2 Safari/605.1.15
- Custom user agent
Custom user agent
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 73954b2c..40c8ba47 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -64,9 +64,12 @@
- @drawable/add_light
- @drawable/create_bookmark_light
- @drawable/create_folder_light
+ - @drawable/copy_light
+ - @drawable/clear_light
- @drawable/select_all_light
- @drawable/edit_light
- @drawable/move_to_folder_light
+ - @drawable/save_light
- @drawable/sort_light
- @style/PrivacyBrowserPopupsLight
- @style/PrivacyBrowserAppBarWhiteText
@@ -158,9 +161,12 @@
- @drawable/add_dark
- @drawable/create_bookmark_dark
- @drawable/create_folder_dark
+ - @drawable/copy_dark
+ - @drawable/clear_dark
- @drawable/select_all_dark
- @drawable/edit_dark
- @drawable/move_to_folder_dark
+ - @drawable/save_dark
- @drawable/sort_dark
- @style/PrivacyBrowserAppBarDark
- @style/PrivacyBrowserTabLayoutDark
--
2.45.2