From 64fe50abeacf25f6fde5d3b3de11801f1618987b Mon Sep 17 00:00:00 2001 From: Soren Stoutner Date: Thu, 25 Oct 2018 11:48:11 -0700 Subject: [PATCH] Add import and export of settings. https://redmine.stoutner.com/issues/23 --- .idea/assetWizardSettings.xml | 5 +- .idea/dictionaries/soren.xml | 2 + app/build.gradle | 6 +- .../dialogs/AdConsentDialog.java | 2 +- .../privacybrowser/helpers/AdHelper.java | 16 +- app/src/free/res/layout/main_webview.xml | 2 +- app/src/free/res/values/strings.xml | 7 +- app/src/main/AndroidManifest.xml | 22 +- .../main/assets/de/about_licenses_dark.html | 1 + .../main/assets/de/about_licenses_light.html | 1 + .../main/assets/en/about_licenses_dark.html | 1 + .../main/assets/en/about_licenses_light.html | 1 + .../main/assets/es/about_licenses_dark.html | 1 + .../main/assets/es/about_licenses_light.html | 1 + .../main/assets/it/about_licenses_dark.html | 1 + .../main/assets/it/about_licenses_light.html | 1 + .../main/assets/ru/about_licenses_dark.html | 1 + .../main/assets/ru/about_licenses_light.html | 1 + .../shared_images/import_export_dark.png | Bin 0 -> 759 bytes .../shared_images/import_export_light.png | Bin 0 -> 736 bytes .../activities/BookmarksActivity.java | 27 +- .../BookmarksDatabaseViewActivity.java | 14 +- .../activities/DomainsActivity.java | 19 +- .../activities/ImportExportActivity.java | 490 +++++++++++++++++ .../activities/MainWebViewActivity.java | 77 +-- .../activities/RequestsActivity.java | 26 +- .../DownloadLocationPermissionDialog.java | 29 +- .../ImportExportStoragePermissionDialog.java | 112 ++++ .../helpers/BookmarksDatabaseHelper.java | 52 +- .../helpers/DomainsDatabaseHelper.java | 86 +-- .../helpers/ImportExportDatabaseHelper.java | 498 ++++++++++++++++++ .../res/drawable/images_disabled_dark.xml | 6 +- .../res/drawable/images_disabled_light.xml | 6 +- .../main/res/drawable/images_enabled_dark.xml | 6 +- .../res/drawable/images_enabled_light.xml | 6 +- .../main/res/drawable/import_export_dark.xml | 18 + .../main/res/drawable/import_export_light.xml | 18 + .../import_export_coordinatorlayout.xml | 210 ++++++++ .../res/layout/requests_coordinatorlayout.xml | 8 +- .../main/res/menu/webview_navigation_menu.xml | 23 +- app/src/main/res/values/strings.xml | 23 +- .../privacybrowser/helpers/AdHelper.java | 4 +- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 +- 44 files changed, 1662 insertions(+), 174 deletions(-) create mode 100644 app/src/main/assets/shared_images/import_export_dark.png create mode 100644 app/src/main/assets/shared_images/import_export_light.png create mode 100644 app/src/main/java/com/stoutner/privacybrowser/activities/ImportExportActivity.java create mode 100644 app/src/main/java/com/stoutner/privacybrowser/dialogs/ImportExportStoragePermissionDialog.java create mode 100644 app/src/main/java/com/stoutner/privacybrowser/helpers/ImportExportDatabaseHelper.java create mode 100644 app/src/main/res/drawable/import_export_dark.xml create mode 100644 app/src/main/res/drawable/import_export_light.xml create mode 100644 app/src/main/res/layout/import_export_coordinatorlayout.xml diff --git a/.idea/assetWizardSettings.xml b/.idea/assetWizardSettings.xml index 58477f94..9f20b9ba 100644 --- a/.idea/assetWizardSettings.xml +++ b/.idea/assetWizardSettings.xml @@ -68,7 +68,7 @@ @@ -78,9 +78,8 @@ diff --git a/.idea/dictionaries/soren.xml b/.idea/dictionaries/soren.xml index 5bed1892..3a9ec86a 100644 --- a/.idea/dictionaries/soren.xml +++ b/.idea/dictionaries/soren.xml @@ -54,6 +54,7 @@ enableformdata enablejavascript enablethirdpartycookies + externalstorage exynos fanboy fanboys @@ -123,6 +124,7 @@ snackbar snackbars softkeyboard + sqlite sslenddate sslissuedbycommonname sslissuedbyorganization diff --git a/app/build.gradle b/app/build.gradle index 25eacaff..88ecb083 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -21,7 +21,7 @@ apply plugin: 'com.android.application' android { compileSdkVersion 28 - buildToolsVersion '28.0.2' + buildToolsVersion '28.0.3' defaultConfig { minSdkVersion 19 @@ -71,10 +71,10 @@ android { dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation 'com.android.support:design:28.0.0-rc02' + implementation 'com.android.support:design:28.0.0' // Only compile Firebase ads for the free flavor. - freeImplementation 'com.google.firebase:firebase-ads:15.0.1' + freeImplementation 'com.google.firebase:firebase-ads:17.0.0' // Only compile the consent library for the free flavor. It is used to comply with the GDPR in Europe. freeImplementation 'com.google.android.ads.consent:consent-library:1.0.6' diff --git a/app/src/free/java/com/stoutner/privacybrowser/dialogs/AdConsentDialog.java b/app/src/free/java/com/stoutner/privacybrowser/dialogs/AdConsentDialog.java index 6b771d6a..8808923a 100644 --- a/app/src/free/java/com/stoutner/privacybrowser/dialogs/AdConsentDialog.java +++ b/app/src/free/java/com/stoutner/privacybrowser/dialogs/AdConsentDialog.java @@ -81,7 +81,7 @@ public class AdConsentDialog extends DialogFragment { consentInformation.setTagForUnderAgeOfConsent(true); // Load an ad. - AdHelper.loadAd(getActivity().findViewById(R.id.adview), getActivity().getApplicationContext(), getString(R.string.ad_id)); + AdHelper.loadAd(getActivity().findViewById(R.id.adview), getActivity().getApplicationContext(), getString(R.string.ad_unit_id)); }); // Return the alert dialog. diff --git a/app/src/free/java/com/stoutner/privacybrowser/helpers/AdHelper.java b/app/src/free/java/com/stoutner/privacybrowser/helpers/AdHelper.java index a4f5b038..24112070 100644 --- a/app/src/free/java/com/stoutner/privacybrowser/helpers/AdHelper.java +++ b/app/src/free/java/com/stoutner/privacybrowser/helpers/AdHelper.java @@ -40,10 +40,10 @@ import com.stoutner.privacybrowser.dialogs.AdConsentDialog; public class AdHelper { private static boolean initialized; - public static void initializeAds (View view, Context applicationContext, FragmentManager fragmentManager, String adId) { + public static void initializeAds (View view, Context applicationContext, FragmentManager fragmentManager, String googleAppId, String adUnitId) { if (!initialized) { // This is the first run. // Initialize mobile ads. - MobileAds.initialize(applicationContext, adId); + MobileAds.initialize(applicationContext, googleAppId); // Store the publisher ID in a string array. String[] publisherIds = {"pub-5962503714887045"}; @@ -62,7 +62,7 @@ public class AdHelper { consentInformation.setTagForUnderAgeOfConsent(true); // Load an ad. - loadAd(view, applicationContext, adId); + loadAd(view, applicationContext, adUnitId); } } @@ -72,7 +72,7 @@ public class AdHelper { consentInformation.setTagForUnderAgeOfConsent(true); // Load an ad. - loadAd(view, applicationContext, adId); + loadAd(view, applicationContext, adUnitId); } }); @@ -80,11 +80,11 @@ public class AdHelper { initialized = true; } else { // Ads have previously been initialized. // Load an ad. - loadAd(view, applicationContext, adId); + loadAd(view, applicationContext, adUnitId); } } - public static void loadAd (View view, Context applicationContext, String adId) { + public static void loadAd (View view, Context applicationContext, String adUnitId) { // Cast the generic view to an AdView. AdView adView = (AdView) view; @@ -98,7 +98,7 @@ public class AdHelper { // Setup the new AdView. This is necessary because the size of the banner ad can change on rotate. adView = new AdView(applicationContext); adView.setAdSize(AdSize.SMART_BANNER); - adView.setAdUnitId(adId); + adView.setAdUnitId(adUnitId); adView.setId(R.id.adview); adView.setLayoutParams(adViewLayoutParameters); @@ -111,6 +111,8 @@ public class AdHelper { // Request a new ad. AdRequest adRequest = new AdRequest.Builder().addNetworkExtrasBundle(AdMobAdapter.class, adSettingsBundle).build(); + // Pixel 2 XL test ads. + // AdRequest adRequest = new AdRequest.Builder().addTestDevice("137D42984218CEECDFD11927BB7D6416").addNetworkExtrasBundle(AdMobAdapter.class, adSettingsBundle).build(); adView.loadAd(adRequest); } diff --git a/app/src/free/res/layout/main_webview.xml b/app/src/free/res/layout/main_webview.xml index fb80533e..49c7ed83 100644 --- a/app/src/free/res/layout/main_webview.xml +++ b/app/src/free/res/layout/main_webview.xml @@ -39,7 +39,7 @@ android:layout_centerHorizontal="true" android:layout_alignParentBottom="true" ads:adSize="SMART_BANNER" - ads:adUnitId="@string/ad_id" > + ads:adUnitId="@string/ad_unit_id" > Privacy Browser Free - ca-app-pub-5962503714887045/2738552414 + ca-app-pub-5962503714887045~2738552414 + ca-app-pub-5962503714887045/2738552414 + ca-app-pub-3940256099942544~3347511713 + ca-app-pub-3940256099942544/6300978111 --> Privacy Browser Free displays a banner ad on the bottom of the screen. diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index fc0dc95a..028a7e3b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -28,7 +28,10 @@ - + + + + @@ -57,6 +60,11 @@ android:name="android.webkit.WebView.EnableSafeBrowsing" android:value="false" /> + + + + + diff --git a/app/src/main/assets/de/about_licenses_dark.html b/app/src/main/assets/de/about_licenses_dark.html index 0d1cccb7..a2c3052a 100644 --- a/app/src/main/assets/de/about_licenses_dark.html +++ b/app/src/main/assets/de/about_licenses_dark.html @@ -108,6 +108,7 @@

home.

image.

import_contacts.

+

import_export.

important_devices.

info_outline.

language.

diff --git a/app/src/main/assets/de/about_licenses_light.html b/app/src/main/assets/de/about_licenses_light.html index 460cc407..5e7008d2 100644 --- a/app/src/main/assets/de/about_licenses_light.html +++ b/app/src/main/assets/de/about_licenses_light.html @@ -108,6 +108,7 @@

home.

image.

import_contacts.

+

import_export.

important_devices.

info_outline.

language.

diff --git a/app/src/main/assets/en/about_licenses_dark.html b/app/src/main/assets/en/about_licenses_dark.html index ff72afdd..453f7011 100644 --- a/app/src/main/assets/en/about_licenses_dark.html +++ b/app/src/main/assets/en/about_licenses_dark.html @@ -106,6 +106,7 @@

home.

image.

import_contacts.

+

import_export.

important_devices.

info_outline.

language.

diff --git a/app/src/main/assets/en/about_licenses_light.html b/app/src/main/assets/en/about_licenses_light.html index ba326c75..63c3af17 100644 --- a/app/src/main/assets/en/about_licenses_light.html +++ b/app/src/main/assets/en/about_licenses_light.html @@ -107,6 +107,7 @@

home.

image.

import_contacts.

+

import_export.

important_devices.

info_outline.

language.

diff --git a/app/src/main/assets/es/about_licenses_dark.html b/app/src/main/assets/es/about_licenses_dark.html index 4c005c08..a8570228 100644 --- a/app/src/main/assets/es/about_licenses_dark.html +++ b/app/src/main/assets/es/about_licenses_dark.html @@ -111,6 +111,7 @@

home.

image.

import_contacts.

+

import_export.

important_devices.

info_outline.

language.

diff --git a/app/src/main/assets/es/about_licenses_light.html b/app/src/main/assets/es/about_licenses_light.html index 3a70f459..5a8eadb9 100644 --- a/app/src/main/assets/es/about_licenses_light.html +++ b/app/src/main/assets/es/about_licenses_light.html @@ -112,6 +112,7 @@

home.

image.

import_contacts.

+

import_export.

important_devices.

info_outline.

language.

diff --git a/app/src/main/assets/it/about_licenses_dark.html b/app/src/main/assets/it/about_licenses_dark.html index 2cba6359..bffcdc26 100644 --- a/app/src/main/assets/it/about_licenses_dark.html +++ b/app/src/main/assets/it/about_licenses_dark.html @@ -115,6 +115,7 @@

home.

image.

import_contacts.

+

import_export.

important_devices.

info_outline.

language.

diff --git a/app/src/main/assets/it/about_licenses_light.html b/app/src/main/assets/it/about_licenses_light.html index dcc575cf..f8cf92dc 100644 --- a/app/src/main/assets/it/about_licenses_light.html +++ b/app/src/main/assets/it/about_licenses_light.html @@ -115,6 +115,7 @@

home.

image.

import_contacts.

+

import_export.

important_devices.

info_outline.

language.

diff --git a/app/src/main/assets/ru/about_licenses_dark.html b/app/src/main/assets/ru/about_licenses_dark.html index e2819da5..b5fe7ae5 100644 --- a/app/src/main/assets/ru/about_licenses_dark.html +++ b/app/src/main/assets/ru/about_licenses_dark.html @@ -108,6 +108,7 @@

home.

image.

import_contacts.

+

import_export.

important_devices.

info_outline.

language.

diff --git a/app/src/main/assets/ru/about_licenses_light.html b/app/src/main/assets/ru/about_licenses_light.html index 6a419a62..264f0d52 100644 --- a/app/src/main/assets/ru/about_licenses_light.html +++ b/app/src/main/assets/ru/about_licenses_light.html @@ -108,6 +108,7 @@

home.

image.

import_contacts.

+

import_export.

important_devices.

info_outline.

language.

diff --git a/app/src/main/assets/shared_images/import_export_dark.png b/app/src/main/assets/shared_images/import_export_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..aae1e03efe67e10e0893cb3bb5d101cf42b2f705 GIT binary patch literal 759 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7uRSEX7WqAsj$Z!;#Vf4nJ z`0WK@#^S6D5ul)CiEBhjaDG}zd16s2LwR|*US?i)adKios$PCk`s{Z$QVa}CC7v#h zAr*7p-nI3+6Clz0@O+@xwh5|BmY>o~Q2a8HO|A0>xAaN9ppE@H4QhWqC+-xInwao7 zyZ-yHJ^r~ftc&AJSf|M_0xg4q3j&_jp4V?acW65NgH_PNQffZO9FJW!iGLoc{jQ0) zp#50*;Iv+u`5fCkE?sT;*nRs=@!}@)K7W>IgW?*c7m-Upb}n$A^iA}*{<}^0#oZz@ zfPx1PJr;I6tx_8aG{o-r-$yg29#?Ogrqb)rqC4S>Y2lwo)!&{cc;@!R3mlwvh| zHIA3IzghbKvEuK~H%+$M)+LG_+;ENC)2g%MvgH0p!7ep&m2d5o>dsZHJ)S60cm7A^ z6}6W+hvGj*eygnhsP~)Uge$`{jsr4`Gn5&MSrYmf&ImSGG8wod3%;xU##p8J(nLwm zE$*KI&*8_Dp08|WIQCmWF!{al4?)3LmIL7!ny{LLBv`de{X_Vlz1Q>JY?W9zn}M(V z=DhUz$=N@+Q0+$2fpF!z+4bz_Lmw^-m0upC{W9}d_QwmWR9Bp350iV$IL$lgwxGd3 zsViwLJ^R(3+HLzM(ZBKh0yCwAKR%~=8$L!)5Mg}0-Q`l<(U0#p-Pe!X5X91Re^aY+ z!XG)0T~P^tZom27%Bkgk;D_xhpu+Dym!9@~%-?jrpX;6kr(vyH(!0_0-R; z_0xGVl|$nGL3^cy8g+L;gMEvgS$4kxCQSse`Of}#QA=~fXFdavpr@;!%Q~loCIFc* BI?Dh6 literal 0 HcmV?d00001 diff --git a/app/src/main/assets/shared_images/import_export_light.png b/app/src/main/assets/shared_images/import_export_light.png new file mode 100644 index 0000000000000000000000000000000000000000..2b70cd7242a7c4279395cceb25361340ee60b143 GIT binary patch literal 736 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7uRSEX7WqAsj$Z!;#Vf4nJ z`0WK@#^S6D5ul)CiEBhjaDG}zd16s2LwR|*US?i)adKios$PCk`s{Z$QVa}Cv7RoD zAr*7p-reoX>?q;-@U-$0RxbthMcf)K5=&T_AKm5fWtjMT0fSHxM?&k7;x~6^)~CJy zx#Vn4W!QHG_J}A>phYmSKyU7n&o7)6+|*xweXPV}JlF2HI+L;cFT1+>SeAL*tba`H z1bt#o{cte|JnkjGf4`oyU)M!lW1!%X2_HKb6ejohv-Hl&-1G7G>s2pg6EZE2sW-Vj zk(tk-RlexYC{PM8H0JakUP{#*X_ z++)AnUdi1zYMR$-tfA8OH$`#u>`Ug4^Y48`^TbZ~51}U3;%SIKY<4WBbZ4_a*PF zoXGHl3%eey27v`*y!Ug~T>72=HYmQEMVHlJ`WErpj+GC!F|5TjV&}AfvNl&Pw1?&D z{gTe~DWM4f%55X` literal 0 HcmV?d00001 diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksActivity.java index d556b305..d48fe2b3 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksActivity.java @@ -66,22 +66,25 @@ public class BookmarksActivity extends AppCompatActivity implements CreateBookma EditBookmarkFolderDialog.EditBookmarkFolderListener, MoveToFolderDialog.MoveToFolderListener { // `currentFolder` is public static so it can be accessed from `MoveToFolderDialog`. - // It is used in `onCreate`, `onOptionsItemSelected()`, `onBackPressed()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveBookmark()`, `onSaveBookmarkFolder()`, `onMoveToFolder()`, and `loadFolder()`. + // It is used in `onCreate`, `onOptionsItemSelected()`, `onBackPressed()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveBookmark()`, `onSaveBookmarkFolder()`, `onMoveToFolder()`, + // and `loadFolder()`. public static String currentFolder; - // `checkedItemIds` is public static so it can be accessed from `MoveToFolderDialog`. It is also used in `onCreate()`, `onSaveBookmark()`, `onSaveBookmarkFolder()`, `onMoveToFolder()`, and `updateMoveIcons()`. + // `checkedItemIds` is public static so it can be accessed from `MoveToFolderDialog`. It is also used in `onCreate()`, `onSaveBookmark()`, `onSaveBookmarkFolder()`, `onMoveToFolder()`, + // and `updateMoveIcons()`. public static long[] checkedItemIds; - // `bookmarksDatabaseHelper` is used in `onCreate()`, `onOptionsItemSelected()`, `onBackPressed()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveBookmark()`, `onSaveBookmarkFolder()`, `onMoveToFolder()`, `deleteBookmarkFolderContents()`, - // and `loadFolder(). + // `bookmarksDatabaseHelper` is used in `onCreate()`, `onOptionsItemSelected()`, `onBackPressed()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveBookmark()`, `onSaveBookmarkFolder()`, + // `onMoveToFolder()`, `deleteBookmarkFolderContents()`, `loadFolder()`, and `onDestroy()`. private BookmarksDatabaseHelper bookmarksDatabaseHelper; - // `bookmarksListView` is used in `onCreate()`, `onOptionsItemSelected()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveBookmark()`, `onSaveBookmarkFolder()`, `onMoveToFolder()`, `updateMoveIcons()`, `scrollBookmarks()`, - // and `loadFolder()`. + // `bookmarksListView` is used in `onCreate()`, `onOptionsItemSelected()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveBookmark()`, `onSaveBookmarkFolder()`, `onMoveToFolder()`, + // `updateMoveIcons()`, `scrollBookmarks()`, and `loadFolder()`. private ListView bookmarksListView; - // `bookmarksCursor` is used in `onCreate()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveBookmark()`, `onSaveBookmarkFolder()`, `onMoveToFolder()`, `deleteBookmarkFolderContents()`, and `loadFolder()`. + // `bookmarksCursor` is used in `onCreate()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveBookmark()`, `onSaveBookmarkFolder()`, `onMoveToFolder()`, `deleteBookmarkFolderContents()`, + // `loadFolder()`, and `onDestroy()`. private Cursor bookmarksCursor; // `bookmarksCursorAdapter` is used in `onCreate(), `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveBookmark()`, `onSaveBookmarkFolder()`, `onMoveToFolder()`, and `onLoadFolder()`. @@ -1072,4 +1075,14 @@ public class BookmarksActivity extends AppCompatActivity implements CreateBookma appBar.setTitle(currentFolder); } } + + @Override + public void onDestroy() { + // Close the bookmarks cursor and database. + bookmarksCursor.close(); + bookmarksDatabaseHelper.close(); + + // Run the default commands. + super.onDestroy(); + } } \ No newline at end of file diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksDatabaseViewActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksDatabaseViewActivity.java index 006af07a..620715ac 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksDatabaseViewActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksDatabaseViewActivity.java @@ -60,10 +60,10 @@ public class BookmarksDatabaseViewActivity extends AppCompatActivity implements private static final int ALL_FOLDERS_DATABASE_ID = -2; private static final int HOME_FOLDER_DATABASE_ID = -1; - // `bookmarksDatabaseHelper` is used in `onCreate()` and `updateBookmarksListView()`. + // `bookmarksDatabaseHelper` is used in `onCreate()`, `updateBookmarksListView()`, and `onDestroy()`. private BookmarksDatabaseHelper bookmarksDatabaseHelper; - // `bookmarksCursor` is used in `onCreate()`, `updateBookmarksListView()`, `onSaveBookmark()`, and `onSaveBookmarkFolder()`. + // `bookmarksCursor` is used in `onCreate()`, `updateBookmarksListView()`, `onSaveBookmark()`, `onSaveBookmarkFolder()`, and `onDestroy()`. private Cursor bookmarksCursor; // `bookmarksCursorAdapter` is used in `onCreate()`, `onSaveBookmark()`, `onSaveBookmarkFolder()`. @@ -435,4 +435,14 @@ public class BookmarksDatabaseViewActivity extends AppCompatActivity implements // Update the `ListView`. bookmarksCursorAdapter.changeCursor(bookmarksCursor); } + + @Override + public void onDestroy() { + // Close the bookmarks cursor and database. + bookmarksCursor.close(); + bookmarksDatabaseHelper.close(); + + // Run the default commands. + super.onDestroy(); + } } \ No newline at end of file diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/DomainsActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/DomainsActivity.java index 479b47a7..1d9a62a6 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/DomainsActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/DomainsActivity.java @@ -55,6 +55,8 @@ import com.stoutner.privacybrowser.fragments.DomainSettingsFragment; import com.stoutner.privacybrowser.fragments.DomainsListFragment; import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper; +import java.util.Objects; + public class DomainsActivity extends AppCompatActivity implements AddDomainDialog.AddDomainListener { // `twoPanedMode` is public static so it can be accessed from `DomainsListFragment`. It is also used in `onCreate()`, `onCreateOptionsMenu()`, and `populateDomainsListView()`. public static boolean twoPanedMode; @@ -80,7 +82,7 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo // `supportFragmentManager` is used in `onCreate()` and `onCreateOptionsMenu()`. private FragmentManager supportFragmentManager; - // `domainsDatabaseHelper` is used in `onCreate()` and `saveDomainSettings()`. + // `domainsDatabaseHelper` is used in `onCreate()`, `saveDomainSettings()`, and `onDestroy()`. private static DomainsDatabaseHelper domainsDatabaseHelper; // `domainsListView` is used in `onCreate()` and `populateDomainsList()`. @@ -159,12 +161,12 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo final Toolbar domainsAppBar = findViewById(R.id.domains_toolbar); setSupportActionBar(domainsAppBar); - // Display the home arrow on `SupportActionBar`. + // Display the home arrow on the support action bar. ActionBar appBar = getSupportActionBar(); assert appBar != null;// This assert removes the incorrect warning in Android Studio on the following line that `appBar` might be null. appBar.setDisplayHomeAsUpEnabled(true); - // Initialize the database handler. The two `nulls` do not specify the database name or a `CursorFactory`. The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`. + // Initialize the database handler. The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`. domainsDatabaseHelper = new DomainsDatabaseHelper(context, null, null, 0); // Determine if we are in two pane mode. `domain_settings_fragment_container` does not exist on devices with a width less than 900dp. @@ -352,7 +354,7 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo deleteMenuItem.setIcon(R.drawable.delete_blue); // Remove the domain settings fragment. - supportFragmentManager.beginTransaction().remove(supportFragmentManager.findFragmentById(R.id.domain_settings_fragment_container)).commit(); + supportFragmentManager.beginTransaction().remove(Objects.requireNonNull(supportFragmentManager.findFragmentById(R.id.domain_settings_fragment_container))).commit(); } else { // Single-paned mode. // Display `DomainsListFragment`. DomainsListFragment domainsListFragment = new DomainsListFragment(); @@ -813,4 +815,13 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo deleteMenuItem.setIcon(R.drawable.delete_blue); } } + + @Override + public void onDestroy() { + // Close the domains database helper. + domainsDatabaseHelper.close(); + + // Run the default commands. + super.onDestroy(); + } } \ No newline at end of file diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/ImportExportActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/ImportExportActivity.java new file mode 100644 index 00000000..705a662b --- /dev/null +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/ImportExportActivity.java @@ -0,0 +1,490 @@ +/* + * Copyright © 2018 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.DialogFragment; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +import android.provider.DocumentsContract; +import android.support.annotation.NonNull; +import android.support.design.widget.Snackbar; +import android.support.v4.app.ActivityCompat; +import android.support.v4.content.ContextCompat; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +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.dialogs.ImportExportStoragePermissionDialog; +import com.stoutner.privacybrowser.helpers.ImportExportDatabaseHelper; + +import java.io.File; + +public class ImportExportActivity extends AppCompatActivity implements ImportExportStoragePermissionDialog.ImportExportStoragePermissionDialogListener { + private final static int EXPORT_FILE_PICKER_REQUEST_CODE = 1; + private final static int IMPORT_FILE_PICKER_REQUEST_CODE = 2; + private final static int EXPORT_REQUEST_CODE = 3; + private final static int IMPORT_REQUEST_CODE = 4; + + @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.import_export_coordinatorlayout); + + // Use the `SupportActionBar` from `android.support.v7.app.ActionBar` until the minimum API is >= 21. + Toolbar importExportAppBar = findViewById(R.id.import_export_toolbar); + setSupportActionBar(importExportAppBar); + + // Display the home arrow on the support action bar. + ActionBar appBar = getSupportActionBar(); + assert appBar != null;// This assert removes the incorrect warning in Android Studio on the following line that `appBar` might be null. + appBar.setDisplayHomeAsUpEnabled(true); + + // Get handles for the views that need to be modified. + EditText exportFileEditText = findViewById(R.id.export_file_edittext); + Button exportButton = findViewById(R.id.export_button); + EditText importFileEditText = findViewById(R.id.import_file_edittext); + Button importButton = findViewById(R.id.import_button); + TextView storagePermissionTextView = findViewById(R.id.import_export_storage_permission_textview); + + // Initially disable the buttons. + exportButton.setEnabled(false); + importButton.setEnabled(false); + + // Enable the export button when the export file EditText isn't empty. + exportFileEditText.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) { + exportButton.setEnabled(!exportFileEditText.getText().toString().isEmpty()); + } + }); + + // Enable the import button when the export file EditText isn't empty. + importFileEditText.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) { + importButton.setEnabled(!importFileEditText.getText().toString().isEmpty()); + } + }); + + // Set the default download file path if the storage permission has not been granted. + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) { + // Create a string for the external private path. + String EXTERNAL_PRIVATE_PATH = getApplicationContext().getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS) + "/Privacy Browser Backup"; + + // Set the default path. + exportFileEditText.setText(EXTERNAL_PRIVATE_PATH); + importFileEditText.setText(EXTERNAL_PRIVATE_PATH); + } + + // Hide the storage permissions TextView on API < 23 as permissions on older devices are automatically granted. + if (Build.VERSION.SDK_INT < 23) { + storagePermissionTextView.setVisibility(View.GONE); + } + } + + public void exportBrowse(View view) { + // Create the file picker intent. + Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); + + // Set the intent MIME type to include all files. + intent.setType("*/*"); + + // Set the initial export file name. + intent.putExtra(Intent.EXTRA_TITLE, getString(R.string.privacy_browser_backup)); + + // Set the initial directory if API >= 26. + if (Build.VERSION.SDK_INT >= 26) { + intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, Environment.getExternalStorageDirectory()); + } + + // Specify that a file that can be opened is requested. + intent.addCategory(Intent.CATEGORY_OPENABLE); + + // Launch the file picker. + startActivityForResult(intent, EXPORT_FILE_PICKER_REQUEST_CODE); + } + + public void onClickExport(View view) { + // Check to see if the storage permission has been granted. + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { // Storage permission granted. + // Export the settings. + exportSettings(); + } else { // Storage permission not granted. + // Get a handle for the export file EditText. + EditText exportFileEditText = findViewById(R.id.export_file_edittext); + + // Get the export file string. + String exportFileString = exportFileEditText.getText().toString(); + + // Get the external private directory `File`. + File externalPrivateDirectoryFile = getApplicationContext().getExternalFilesDir(null); + + // Remove the 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 export file path is in the external private directory. + if (exportFileString.startsWith(externalPrivateDirectory)) { // The export path is in the external private directory. + // Export the settings. + exportSettings(); + } else { // The export path is in a public directory. + // Check if the user has previously denied the storage permission. + if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { // Show a dialog explaining the request first. + // Instantiate the storage permission alert dialog and set the type to EXPORT_SETTINGS. + DialogFragment importExportStoragePermissionDialogFragment = ImportExportStoragePermissionDialog.type(ImportExportStoragePermissionDialog.EXPORT_SETTINGS); + + // Show the storage permission alert dialog. The permission will be requested when the dialog is closed. + importExportStoragePermissionDialogFragment.show(getFragmentManager(), 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}, EXPORT_REQUEST_CODE); + } + } + } + } + + public void importBrowse(View view) { + // Create the file picker intent. + Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); + + // Set the intent MIME type to include all files. + intent.setType("*/*"); + + // Set the initial directory if API >= 26. + if (Build.VERSION.SDK_INT >= 26) { + intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, Environment.getExternalStorageDirectory()); + } + + // Specify that a file that can be opened is requested. + intent.addCategory(Intent.CATEGORY_OPENABLE); + + // Launch the file picker. + startActivityForResult(intent, IMPORT_FILE_PICKER_REQUEST_CODE); + } + + public void onClickImport(View view) { + // Check to see if the storage permission has been granted. + if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { // Storage permission granted. + // Import the settings. + importSettings(); + } else { // Storage permission not granted. + // Get a handle for the import file EditText. + EditText importFileEditText = findViewById(R.id.import_file_edittext); + + // Get the import file string. + String importFileString = importFileEditText.getText().toString(); + + // Get the external private directory `File`. + File externalPrivateDirectoryFile = getApplicationContext().getExternalFilesDir(null); + + // Remove the lint error below that `File` might be null. + assert externalPrivateDirectoryFile != null; + + // Get the external private directory string. + String externalPrivateDirectory = externalPrivateDirectoryFile.toString(); + + // Check to see if the import file path is in the external private directory. + if (importFileString.startsWith(externalPrivateDirectory)) { // The import path is in the external private directory. + // Import the settings. + importSettings(); + } else { // The import path is in a public directory. + // Check if the user has previously denied the storage permission. + if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_EXTERNAL_STORAGE)) { // Show a dialog explaining the request first. + // Instantiate the storage permission alert dialog and set the type to IMPORT_SETTINGS. + DialogFragment importExportStoragePermissionDialogFragment = ImportExportStoragePermissionDialog.type(ImportExportStoragePermissionDialog.IMPORT_SETTINGS); + + // Show the storage permission alert dialog. The permission will be requested when the dialog is closed. + importExportStoragePermissionDialogFragment.show(getFragmentManager(), 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.READ_EXTERNAL_STORAGE}, IMPORT_REQUEST_CODE); + } + } + } + } + + @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) { + // Run the commands for the specific request code. + switch (requestCode) { + case EXPORT_FILE_PICKER_REQUEST_CODE: + // Get a handle for the export file EditText. + EditText exportFileEditText = findViewById(R.id.export_file_edittext); + + // Get the selected export file. + Uri exportUri = data.getData(); + + // Remove the lint warning that the export URI might be null. + assert exportUri != null; + + // Get the raw export path. + String rawExportPath = exportUri.getPath(); + + // Remove the warning that the raw export path might be null. + assert rawExportPath != null; + + // Check to see if the rawExportPath includes a valid storage location. + if (rawExportPath.contains(":")) { // The path is valid. + // Split the path into the initial content uri and the path information. + String exportContentPath = rawExportPath.substring(0, rawExportPath.indexOf(":")); + String exportFilePath = rawExportPath.substring(rawExportPath.indexOf(":") + 1); + + // Create the export path string. + String exportPath; + + // Construct the export path. + switch (exportContentPath) { + // The documents home has a special content path. + case "/document/home": + exportPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS) + "/" + exportFilePath; + break; + + // Everything else for the primary user should be in `/document/primary`. + case "/document/primary": + exportPath = Environment.getExternalStorageDirectory() + "/" + exportFilePath; + break; + + // Just in case, catch everything else and place it in the external storage directory. + default: + exportPath = Environment.getExternalStorageDirectory() + "/" + exportFilePath; + break; + } + + // Set the export file URI as the text for the export file EditText. + exportFileEditText.setText(exportPath); + } else { // The path is invalid. + Snackbar.make(exportFileEditText, rawExportPath + " + " + getString(R.string.invalid_location), Snackbar.LENGTH_INDEFINITE).show(); + } + break; + + case IMPORT_FILE_PICKER_REQUEST_CODE: + // Get a handle for the import file EditText. + EditText importFileEditText = findViewById(R.id.import_file_edittext); + + // Get the selected import file. + Uri importUri = data.getData(); + + // Remove the lint warning that the import URI might be null. + assert importUri != null; + + // Get the raw import path. + String rawImportPath = importUri.getPath(); + + // Remove the warning that the raw import path might be null. + assert rawImportPath != null; + + // Check to see if the rawExportPath includes a valid storage location. + if (rawImportPath.contains(":")) { // The path is valid. + // Split the path into the initial content uri and the path information. + String importContentPath = rawImportPath.substring(0, rawImportPath.indexOf(":")); + String importFilePath = rawImportPath.substring(rawImportPath.indexOf(":") + 1); + + // Create the export path string. + String importPath; + + // Construct the export path. + switch (importContentPath) { + // The documents folder has a special content path. + case "/document/home": + importPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS) + "/" + importFilePath; + break; + + // Everything else for the primary user should be in `/document/primary`. + case "/document/primary": + importPath = Environment.getExternalStorageDirectory() + "/" + importFilePath; + break; + + // Just in case, catch everything else and place it in the external storage directory. + default: + importPath = Environment.getExternalStorageDirectory() + "/" + importFilePath; + break; + } + + // Set the export file URI as the text for the export file EditText. + importFileEditText.setText(importPath); + } else { // The path is invalid. + Snackbar.make(importFileEditText, rawImportPath + " + " + getString(R.string.invalid_location), Snackbar.LENGTH_INDEFINITE).show(); + } + break; + } + } + } + + @Override + public void onCloseImportExportStoragePermissionDialog(int type) { + // Request the storage permission based on the button that was pressed. + switch (type) { + case ImportExportStoragePermissionDialog.EXPORT_SETTINGS: + // Request the storage permission. The export will be run when it finishes. + ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, EXPORT_REQUEST_CODE); + break; + + case ImportExportStoragePermissionDialog.IMPORT_SETTINGS: + // Request the storage permission. The import will be run when it finishes. + ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.READ_EXTERNAL_STORAGE}, IMPORT_REQUEST_CODE); + break; + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + switch (requestCode) { + case EXPORT_REQUEST_CODE: + // Check to see if the storage permission was granted. If the dialog was canceled the grant results will be empty. + if ((grantResults.length > 0) && (grantResults[0] == PackageManager.PERMISSION_GRANTED)) { // The storage permission was granted. + // Export the settings. + exportSettings(); + } else { // The storage permission was not granted. + // Get a handle for the export file EditText. + EditText exportFileEditText = findViewById(R.id.export_file_edittext); + + // Display an error snackbar. + Snackbar.make(exportFileEditText, getString(R.string.cannot_export), Snackbar.LENGTH_LONG).show(); + } + break; + + case IMPORT_REQUEST_CODE: + // Check to see if the storage permission was granted. If the dialog was canceled the grant results will be empty. + if ((grantResults.length > 0) && (grantResults[0] == PackageManager.PERMISSION_GRANTED)) { // The storage permission was granted. + // Import the settings. + importSettings(); + } else { // The storage permission was not granted. + // Get a handle for the import file EditText. + EditText importFileEditText = findViewById(R.id.import_file_edittext); + + // Display an error snackbar. + Snackbar.make(importFileEditText, getString(R.string.cannot_import), Snackbar.LENGTH_LONG).show(); + } + break; + } + } + + private void exportSettings() { + // Get a handle for the export file EditText. + EditText exportFileEditText = findViewById(R.id.export_file_edittext); + + // Get the export file string. + String exportFileString = exportFileEditText.getText().toString(); + + // Set the export file. + File exportFile = new File(exportFileString); + + // Instantiate the import export database helper. + ImportExportDatabaseHelper importExportDatabaseHelper = new ImportExportDatabaseHelper(); + + // Export the unencrypted file. + String exportStatus = importExportDatabaseHelper.exportUnencrypted(exportFile, getApplicationContext()); + + // Show a disposition snackbar. + if (exportStatus.equals(ImportExportDatabaseHelper.EXPORT_SUCCESSFUL)) { + Snackbar.make(exportFileEditText, getString(R.string.export_successful), Snackbar.LENGTH_SHORT).show(); + } else { + Snackbar.make(exportFileEditText, getString(R.string.export_failed) + " " + exportStatus, Snackbar.LENGTH_INDEFINITE).show(); + } + } + + private void importSettings() { + // Get a handle for the import file EditText. + EditText importFileEditText = findViewById(R.id.import_file_edittext); + + // Get the import file string. + String importFileString = importFileEditText.getText().toString(); + + // Set the import file. + File importFile = new File(importFileString); + + // Instantiate the import export database helper. + ImportExportDatabaseHelper importExportDatabaseHelper = new ImportExportDatabaseHelper(); + + // Import the unencrypted file. + String importStatus = importExportDatabaseHelper.importUnencrypted(importFile, getApplicationContext()); + + // Respond to the import disposition. + if (importStatus.equals(ImportExportDatabaseHelper.IMPORT_SUCCESSFUL)) { // The import was successful. + // Create an intent to restart Privacy Browser. + Intent restartIntent = getParentActivityIntent(); + + // Assert that the intent is not null to remove the lint error below. + assert restartIntent != null; + + // `Intent.FLAG_ACTIVITY_CLEAR_TASK` removes all activities from the stack. It requires `Intent.FLAG_ACTIVITY_NEW_TASK`. + restartIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + + // Make it so. + startActivity(restartIntent); + } else { // The import was not successful. + // Display a snack bar with the import error. + Snackbar.make(importFileEditText, getString(R.string.import_failed) + " " + importStatus, Snackbar.LENGTH_INDEFINITE).show(); + } + } +} \ 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 230fc499..f2a422a2 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java @@ -202,13 +202,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // The request items are public static so they can be accessed by `BlockListHelper`, `RequestsArrayAdapter`, and `ViewRequestsDialog`. They are also used in `onCreate()` and `onPrepareOptionsMenu()`. public static List resourceRequests; public static String[] whiteListResultStringArray; - int blockedRequests; - int easyListBlockedRequests; - int easyPrivacyBlockedRequests; - int fanboysAnnoyanceListBlockedRequests; - int fanboysSocialBlockingListBlockedRequests; - int ultraPrivacyBlockedRequests; - int thirdPartyBlockedRequests; + private int blockedRequests; + private int easyListBlockedRequests; + private int easyPrivacyBlockedRequests; + private int fanboysAnnoyanceListBlockedRequests; + private int fanboysSocialBlockingListBlockedRequests; + private int ultraPrivacyBlockedRequests; + private int thirdPartyBlockedRequests; public final static int REQUEST_DISPOSITION = 0; public final static int REQUEST_URL = 1; @@ -353,7 +353,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook private MenuItem fanboysAnnoyanceListMenuItem; private MenuItem fanboysSocialBlockingListMenuItem; private MenuItem ultraPrivacyMenuItem; - private MenuItem blockAllThirdParyRequestsMenuItem; + private MenuItem blockAllThirdPartyRequestsMenuItem; // The blocklist variables are used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyAppSettings()`. private boolean easyListEnabled; @@ -404,7 +404,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `ignorePinnedSslCertificateForDomain` is used in `onCreate()`, `onSslMismatchProceed()`, and `applyDomainSettings()`. private boolean ignorePinnedSslCertificate; - // `orbotStatusBroadcastReciever` is used in `onCreate()` and `onDestroy()`. + // `orbotStatusBroadcastReceiver` is used in `onCreate()` and `onDestroy()`. private BroadcastReceiver orbotStatusBroadcastReceiver; // `waitingForOrbot` is used in `onCreate()`, `onResume()`, and `applyAppSettings()`. @@ -469,7 +469,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `pinnedDomainSslCertificate` is used in `onCreate()` and `applyDomainSettings()`. private boolean pinnedDomainSslCertificate; - // `bookmarksDatabaseHelper` is used in `onCreate()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and `loadBookmarksFolder()`. + // `bookmarksDatabaseHelper` is used in `onCreate()`, `onDestroy`, `onOptionsItemSelected()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, + // and `loadBookmarksFolder()`. private BookmarksDatabaseHelper bookmarksDatabaseHelper; // `bookmarksListView` is used in `onCreate()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, and `loadBookmarksFolder()`. @@ -478,7 +479,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `bookmarksTitleTextView` is used in `onCreate()` and `loadBookmarksFolder()`. private TextView bookmarksTitleTextView; - // `bookmarksCursor` is used in `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and `loadBookmarksFolder()`. + // `bookmarksCursor` is used in `onDestroy()`, `onOptionsItemSelected()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and `loadBookmarksFolder()`. private Cursor bookmarksCursor; // `bookmarksCursorAdapter` is used in `onCreateBookmark()`, `onCreateBookmarkFolder()` `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and `loadBookmarksFolder()`. @@ -728,7 +729,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Show the `BannerAd` in the free flavor. if (BuildConfig.FLAVOR.contentEquals("free")) { // Reload the ad. The AdView is destroyed and recreated, which changes the ID, every time it is reloaded to handle possible rotations. - AdHelper.loadAd(findViewById(R.id.adview), getApplicationContext(), getString(R.string.ad_id)); + AdHelper.loadAd(findViewById(R.id.adview), getApplicationContext(), getString(R.string.ad_unit_id)); } // Remove the translucent navigation bar flag if it is set. @@ -949,10 +950,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook mainWebView.evaluateJavascript("(function() {var parent = document.getElementsByTagName('head').item(0); var style = document.createElement('style'); style.type = 'text/css'; " + "style.innerHTML = '* {background-color: #212121 !important; color: #BDBDBD !important; box-shadow: none !important; text-decoration: none !important;" + "text-shadow: none !important; border: none !important;} a {color: #1565C0 !important;}'; parent.appendChild(style)})()", value -> { - // Initialize a `Handler` to display `mainWebView`. + // Initialize a handler to display `mainWebView`. Handler displayWebViewHandler = new Handler(); - // Setup a `Runnable` to display `mainWebView` after a delay to allow the CSS to be applied. + // Setup a runnable to display `mainWebView` after a delay to allow the CSS to be applied. Runnable displayWebViewRunnable = () -> { // Only display `mainWebView` if the progress bar is one. This prevents the display of the `WebView` while it is still loading. if (progressBar.getVisibility() == View.GONE) { @@ -960,7 +961,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } }; - // Use `displayWebViewHandler` to delay the displaying of `mainWebView` for 500 milliseconds. + // Displaying of `mainWebView` after 500 milliseconds. displayWebViewHandler.postDelayed(displayWebViewRunnable, 500); }); } @@ -1097,7 +1098,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Show the `BannerAd` in the free flavor. if (BuildConfig.FLAVOR.contentEquals("free")) { // Initialize the ad. The AdView is destroyed and recreated, which changes the ID, every time it is reloaded to handle possible rotations. - AdHelper.initializeAds(findViewById(R.id.adview), getApplicationContext(), getFragmentManager(), getString(R.string.ad_id)); + AdHelper.initializeAds(findViewById(R.id.adview), getApplicationContext(), getFragmentManager(), getString(R.string.google_app_id), getString(R.string.ad_unit_id)); } // Remove any `SYSTEM_UI` flags from `rootCoordinatorLayout`. @@ -1116,7 +1117,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Show the ad if this is the free flavor. if (BuildConfig.FLAVOR.contentEquals("free")) { // Reload the ad. The AdView is destroyed and recreated, which changes the ID, every time it is reloaded to handle possible rotations. - AdHelper.loadAd(findViewById(R.id.adview), getApplicationContext(), getString(R.string.ad_id)); + AdHelper.loadAd(findViewById(R.id.adview), getApplicationContext(), getString(R.string.ad_unit_id)); } } @@ -1154,7 +1155,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Show a dialog if the user has previously denied the permission. if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { // Show a dialog explaining the request first. - // Get a handle for the download location permission alert dialog and set the download type to DOWNLOAD_FILE. + // Instantiate the download location permission alert dialog and set the download type to DOWNLOAD_FILE. DialogFragment downloadLocationPermissionDialogFragment = DownloadLocationPermissionDialog.downloadType(DownloadLocationPermissionDialog.DOWNLOAD_FILE); // Show the download location permission alert dialog. The permission will be requested when the the dialog is closed. @@ -1163,7 +1164,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Request the permission. The download dialog will be launched by `onRequestPermissionResult()`. ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, DOWNLOAD_FILE_REQUEST_CODE); } - } else { // The WRITE_EXTERNAL_STORAGE permission has already been granted. + } else { // The storage permission has already been granted. // Get a handle for the download file alert dialog. AppCompatDialogFragment downloadFileDialogFragment = DownloadFileDialog.fromUrl(url, contentDisposition, contentLength); @@ -1400,7 +1401,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook activity.runOnUiThread(() -> { navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests); blocklistsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests); - blockAllThirdParyRequestsMenuItem.setTitle(thirdPartyBlockedRequests + " - " + getString(R.string.block_all_third_party_requests)); + blockAllThirdPartyRequestsMenuItem.setTitle(thirdPartyBlockedRequests + " - " + getString(R.string.block_all_third_party_requests)); }); // Add the request to the log. @@ -1526,7 +1527,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Add the request to the log because it hasn't been processed by any of the previous checks. if (whiteListResultStringArray != null ) { // The request was processed by a whitelist. resourceRequests.add(whiteListResultStringArray); - } else { // The request didn't match any blocklist entry. Log it as a defult request. + } else { // The request didn't match any blocklist entry. Log it as a default request. resourceRequests.add(new String[]{String.valueOf(REQUEST_DEFAULT), url}); } @@ -1960,6 +1961,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Unregister the Orbot status broadcast receiver. this.unregisterReceiver(orbotStatusBroadcastReceiver); + // Close the bookmarks cursor and database. + bookmarksCursor.close(); + bookmarksDatabaseHelper.close(); + // Run the default commands. super.onDestroy(); } @@ -1988,7 +1993,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook fanboysAnnoyanceListMenuItem = menu.findItem(R.id.fanboys_annoyance_list); fanboysSocialBlockingListMenuItem = menu.findItem(R.id.fanboys_social_blocking_list); ultraPrivacyMenuItem = menu.findItem(R.id.ultraprivacy); - blockAllThirdParyRequestsMenuItem = menu.findItem(R.id.block_all_third_party_requests); + blockAllThirdPartyRequestsMenuItem = menu.findItem(R.id.block_all_third_party_requests); MenuItem adConsentMenuItem = menu.findItem(R.id.ad_consent); // Only display third-party cookies if API >= 21 @@ -2070,7 +2075,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook fanboysAnnoyanceListMenuItem.setChecked(fanboysAnnoyanceListEnabled); fanboysSocialBlockingListMenuItem.setChecked(fanboysSocialBlockingListEnabled); ultraPrivacyMenuItem.setChecked(ultraPrivacyEnabled); - blockAllThirdParyRequestsMenuItem.setChecked(blockAllThirdPartyRequests); + blockAllThirdPartyRequestsMenuItem.setChecked(blockAllThirdPartyRequests); swipeToRefreshMenuItem.setChecked(swipeRefreshLayout.isEnabled()); displayImagesMenuItem.setChecked(mainWebView.getSettings().getLoadsImagesAutomatically()); nightModeMenuItem.setChecked(nightMode); @@ -2123,7 +2128,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook fanboysAnnoyanceListMenuItem.setTitle(fanboysAnnoyanceListBlockedRequests + " - " + getString(R.string.fanboys_annoyance_list)); fanboysSocialBlockingListMenuItem.setTitle(fanboysSocialBlockingListBlockedRequests + " - " + getString(R.string.fanboys_social_blocking_list)); ultraPrivacyMenuItem.setTitle(ultraPrivacyBlockedRequests + " - " + getString(R.string.ultraprivacy)); - blockAllThirdParyRequestsMenuItem.setTitle(thirdPartyBlockedRequests + " - " + getString(R.string.block_all_third_party_requests)); + blockAllThirdPartyRequestsMenuItem.setTitle(thirdPartyBlockedRequests + " - " + getString(R.string.block_all_third_party_requests)); // Get the current user agent. String currentUserAgent = mainWebView.getSettings().getUserAgentString(); @@ -2908,6 +2913,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook startActivity(settingsIntent); break; + case R.id.import_export: + // Launch the import/export activity. + Intent importExportIntent = new Intent (this, ImportExportActivity.class); + startActivity(importExportIntent); + break; + case R.id.guide: // Launch `GuideActivity`. Intent guideIntent = new Intent(this, GuideActivity.class); @@ -2921,6 +2932,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook break; case R.id.clearAndExit: + // Close the bookmarks cursor and database. + bookmarksCursor.close(); + bookmarksDatabaseHelper.close(); + // Get a handle for `sharedPreferences`. `this` references the current context. SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); @@ -3059,7 +3074,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Reload the ad for the free flavor if we not in full screen mode. if (BuildConfig.FLAVOR.contentEquals("free") && !inFullScreenBrowsingMode) { // Reload the ad. The AdView is destroyed and recreated, which changes the ID, every time it is reloaded to handle possible rotations. - AdHelper.loadAd(findViewById(R.id.adview), getApplicationContext(), getString(R.string.ad_id)); + AdHelper.loadAd(findViewById(R.id.adview), getApplicationContext(), getString(R.string.ad_unit_id)); } // `invalidateOptionsMenu` should recalculate the number of action buttons from the menu to display on the app bar, but it doesn't because of the this bug: @@ -3120,7 +3135,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Show a dialog if the user has previously denied the permission. if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { // Show a dialog explaining the request first. - // Get a handle for the download location permission alert dialog and set the download type to DOWNLOAD_FILE. + // Instantiate the download location permission alert dialog and set the download type to DOWNLOAD_FILE. DialogFragment downloadLocationPermissionDialogFragment = DownloadLocationPermissionDialog.downloadType(DownloadLocationPermissionDialog.DOWNLOAD_FILE); // Show the download location permission alert dialog. The permission will be requested when the the dialog is closed. @@ -3205,7 +3220,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Show a dialog if the user has previously denied the permission. if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { // Show a dialog explaining the request first. - // Get a handle for the download location permission alert dialog and set the download type to DOWNLOAD_IMAGE. + // Instantiate the download location permission alert dialog and set the download type to DOWNLOAD_IMAGE. DialogFragment downloadLocationPermissionDialogFragment = DownloadLocationPermissionDialog.downloadType(DownloadLocationPermissionDialog.DOWNLOAD_IMAGE); // Show the download location permission alert dialog. The permission will be requested when the dialog is closed. @@ -3264,7 +3279,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Show a dialog if the user has previously denied the permission. if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { // Show a dialog explaining the request first. - // Get a handle for the download location permission alert dialog and set the download type to DOWNLOAD_IMAGE. + // Instantiate the download location permission alert dialog and set the download type to DOWNLOAD_IMAGE. DialogFragment downloadLocationPermissionDialogFragment = DownloadLocationPermissionDialog.downloadType(DownloadLocationPermissionDialog.DOWNLOAD_IMAGE); // Show the download location permission alert dialog. The permission will be requested when the dialog is closed. @@ -3417,7 +3432,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode) { case DOWNLOAD_FILE_REQUEST_CODE: // Show the download file alert dialog. When the dialog closes, the correct command will be used based on the permission status. @@ -3997,7 +4012,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Show the `BannerAd` in the free flavor. if (BuildConfig.FLAVOR.contentEquals("free")) { // Initialize the ad. The AdView is destroyed and recreated, which changes the ID, every time it is reloaded to handle possible rotations. - AdHelper.initializeAds(findViewById(R.id.adview), getApplicationContext(), getFragmentManager(), getString(R.string.ad_id)); + AdHelper.initializeAds(findViewById(R.id.adview), getApplicationContext(), getFragmentManager(), getString(R.string.google_app_id), getString(R.string.ad_unit_id)); } // Remove any `SYSTEM_UI` flags from `rootCoordinatorLayout`. @@ -4364,7 +4379,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook urlAppBarRelativeLayout.setBackgroundDrawable(getResources().getDrawable(R.color.transparent)); } - // Close `domainsDatabaseHelper`. + // Close the domains database helper. domainsDatabaseHelper.close(); // Remove the `onTheFlyDisplayImagesSet` flag and set the display webpage images mode. `true` indicates that custom domain settings are applied. 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 4b58053a..a62708a3 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/RequestsActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/RequestsActivity.java @@ -45,7 +45,7 @@ import java.util.List; public class RequestsActivity extends AppCompatActivity implements ViewRequestDialog.ViewRequestListener { // The list view is used in `onCreate()` and `launchViewRequestDialog()`. - private ListView resourceRequestsListView; + private ListView requestsListView; @Override public void onCreate(Bundle savedInstanceState) { @@ -68,12 +68,12 @@ public class RequestsActivity extends AppCompatActivity implements ViewRequestDi setContentView(R.layout.requests_coordinatorlayout); // Use the `SupportActionBar` from `android.support.v7.app.ActionBar` until the minimum API is >= 21. - Toolbar blockListsAppBar = findViewById(R.id.blocklists_toolbar); - setSupportActionBar(blockListsAppBar); + Toolbar requestsAppBar = findViewById(R.id.requests_toolbar); + setSupportActionBar(requestsAppBar); // Get a handle for the app bar and the list view. ActionBar appBar = getSupportActionBar(); - resourceRequestsListView = findViewById(R.id.resource_requests_listview); + requestsListView = findViewById(R.id.requests_listview); // Remove the incorrect lint warning that `appBar` might be null. assert appBar != null; @@ -165,7 +165,7 @@ public class RequestsActivity extends AppCompatActivity implements ViewRequestDi ArrayAdapter allResourceRequestsArrayAdapter = new RequestsArrayAdapter(getApplicationContext(), allResourceRequests); // Display the adapter in the list view. - resourceRequestsListView.setAdapter(allResourceRequestsArrayAdapter); + requestsListView.setAdapter(allResourceRequestsArrayAdapter); break; case 1: // Default requests. @@ -173,7 +173,7 @@ public class RequestsActivity extends AppCompatActivity implements ViewRequestDi ArrayAdapter defaultResourceRequestsArrayAdapter = new RequestsArrayAdapter(getApplicationContext(), defaultResourceRequests); // Display the adapter in the list view. - resourceRequestsListView.setAdapter(defaultResourceRequestsArrayAdapter); + requestsListView.setAdapter(defaultResourceRequestsArrayAdapter); break; case 2: // Allowed requests. @@ -181,7 +181,7 @@ public class RequestsActivity extends AppCompatActivity implements ViewRequestDi ArrayAdapter allowedResourceRequestsArrayAdapter = new RequestsArrayAdapter(getApplicationContext(), allowedResourceRequests); // Display the adapter in the list view. - resourceRequestsListView.setAdapter(allowedResourceRequestsArrayAdapter); + requestsListView.setAdapter(allowedResourceRequestsArrayAdapter); break; case 3: // Third-party requests. @@ -189,7 +189,7 @@ public class RequestsActivity extends AppCompatActivity implements ViewRequestDi ArrayAdapter thirdPartyResourceRequestsArrayAdapter = new RequestsArrayAdapter(getApplicationContext(), thirdPartyResourceRequests); //Display the adapter in the list view. - resourceRequestsListView.setAdapter(thirdPartyResourceRequestsArrayAdapter); + requestsListView.setAdapter(thirdPartyResourceRequestsArrayAdapter); break; case 4: // Blocked requests. @@ -197,7 +197,7 @@ public class RequestsActivity extends AppCompatActivity implements ViewRequestDi ArrayAdapter blockedResourceRequestsArrayAdapter = new RequestsArrayAdapter(getApplicationContext(), blockedResourceRequests); // Display the adapter in the list view. - resourceRequestsListView.setAdapter(blockedResourceRequestsArrayAdapter); + requestsListView.setAdapter(blockedResourceRequestsArrayAdapter); break; } } @@ -212,10 +212,10 @@ public class RequestsActivity extends AppCompatActivity implements ViewRequestDi ArrayAdapter resourceRequestsArrayAdapter = new RequestsArrayAdapter(getApplicationContext(), allResourceRequests); // Populate the list view with the resource requests adapter. - resourceRequestsListView.setAdapter(resourceRequestsArrayAdapter); + requestsListView.setAdapter(resourceRequestsArrayAdapter); // Listen for taps on entries in the list view. - resourceRequestsListView.setOnItemClickListener((AdapterView parent, View view, int position, long id) -> { + requestsListView.setOnItemClickListener((AdapterView parent, View view, int position, long id) -> { // Display the view request dialog. The list view is 0 based, so the position must be incremented by 1. launchViewRequestDialog(position + 1); }); @@ -235,10 +235,10 @@ public class RequestsActivity extends AppCompatActivity implements ViewRequestDi private void launchViewRequestDialog(int id) { // Determine if this is the last request in the list. - boolean isLastRequest = (id == resourceRequestsListView.getCount()); + boolean isLastRequest = (id == requestsListView.getCount()); // Get the string array for the selected resource request. The resource requests list view is zero based. - String[] selectedRequestStringArray = (String[]) resourceRequestsListView.getItemAtPosition(id - 1); + String[] selectedRequestStringArray = (String[]) requestsListView.getItemAtPosition(id - 1); // Remove the warning that `selectedRequest` might be null. assert selectedRequestStringArray != null; diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/DownloadLocationPermissionDialog.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/DownloadLocationPermissionDialog.java index 117f000b..a2320588 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/DownloadLocationPermissionDialog.java +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/DownloadLocationPermissionDialog.java @@ -31,10 +31,11 @@ import com.stoutner.privacybrowser.R; import com.stoutner.privacybrowser.activities.MainWebViewActivity; public class DownloadLocationPermissionDialog extends DialogFragment { + // The constants are used to differentiate between the two download types. public static final int DOWNLOAD_FILE = 1; public static final int DOWNLOAD_IMAGE = 2; - // `downloadLocationPermissionDialogListener` is used in `onAttach()` and `onCreateDialog()`. + // The listener is used in `onAttach()` and `onCreateDialog()`. private DownloadLocationPermissionDialogListener downloadLocationPermissionDialogListener; // The public interface is used to send information back to the parent activity. @@ -47,7 +48,7 @@ public class DownloadLocationPermissionDialog extends DialogFragment { // Run the default commands. super.onAttach(context); - // Get a handle for `DownloadLocationPermissionDialogListener` from the launching context. + // Get a handle for the listener from the launching context. downloadLocationPermissionDialogListener = (DownloadLocationPermissionDialogListener) context; } @@ -56,9 +57,9 @@ public class DownloadLocationPermissionDialog extends DialogFragment { Bundle argumentsBundle = new Bundle(); // Store the download type in the bundle. - argumentsBundle.putInt("Download_Type", type); + argumentsBundle.putInt("download_type", type); - // Add the arguments bundle to this instance of `DownloadLocationPermissionDialog`. + // Add the arguments bundle to this instance of the dialog. DownloadLocationPermissionDialog thisDownloadLocationPermissionDialog = new DownloadLocationPermissionDialog(); thisDownloadLocationPermissionDialog.setArguments(argumentsBundle); return thisDownloadLocationPermissionDialog; @@ -66,8 +67,8 @@ public class DownloadLocationPermissionDialog extends DialogFragment { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { - // Store the download type in the local class variable. - int downloadType = getArguments().getInt("Download_Type"); + // Store the download type in a local variable. + int downloadType = getArguments().getInt("download_type"); // Use a builder to create the alert dialog. AlertDialog.Builder dialogBuilder; @@ -81,19 +82,19 @@ public class DownloadLocationPermissionDialog extends DialogFragment { dialogBuilder.setIcon(R.drawable.downloads_light); } - // Set an `onClick` listener on the negative button. Using `null` as the listener closes the dialog without doing anything else. - dialogBuilder.setNegativeButton(R.string.ok, (DialogInterface dialog, int which) -> { - // Inform the parent activity that the dialog was closed. - downloadLocationPermissionDialogListener.onCloseDownloadLocationPermissionDialog(downloadType); - }); - // Set the title. dialogBuilder.setTitle(R.string.download_location); // Set the text. dialogBuilder.setMessage(R.string.download_location_message); - // Create an alert dialog from the alert dialog builder. + // 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. + downloadLocationPermissionDialogListener.onCloseDownloadLocationPermissionDialog(downloadType); + }); + + // Create an alert dialog from the builder. final AlertDialog alertDialog = dialogBuilder.create(); // Disable screenshots if not allowed. @@ -105,7 +106,7 @@ public class DownloadLocationPermissionDialog extends DialogFragment { alertDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE); } - // `onCreateDialog` requires the return of an `AlertDialog`. + // `onCreateDialog()` requires the return of an `AlertDialog`. return alertDialog; } } \ No newline at end of file diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/ImportExportStoragePermissionDialog.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/ImportExportStoragePermissionDialog.java new file mode 100644 index 00000000..d5b93900 --- /dev/null +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/ImportExportStoragePermissionDialog.java @@ -0,0 +1,112 @@ +/* + * Copyright © 2018 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.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.view.WindowManager; + +import com.stoutner.privacybrowser.R; +import com.stoutner.privacybrowser.activities.MainWebViewActivity; + +public class ImportExportStoragePermissionDialog extends DialogFragment { + // The constants are used to differentiate between the two commands. + public static final int EXPORT_SETTINGS = 1; + public static final int IMPORT_SETTINGS = 2; + + // The listener is used in `onAttach()` and `onCreateDialog()`. + private ImportExportStoragePermissionDialogListener importExportStoragePermissionDialogListener; + + // The public interface is used to send information back to the parent activity. + public interface ImportExportStoragePermissionDialogListener { + void onCloseImportExportStoragePermissionDialog(int type); + } + + @Override + public void onAttach(Context context) { + // Run the default commands. + super.onAttach(context); + + // Get a handle for the listener from the launching context. + importExportStoragePermissionDialogListener = (ImportExportStoragePermissionDialogListener) context; + } + + public static ImportExportStoragePermissionDialog type(int type) { + // Create an arguments bundle. + Bundle argumentsBundle = new Bundle(); + + // Store the download type in the bundle. + argumentsBundle.putInt("type", type); + + // Add the arguments bundle to this instance of the dialog. + ImportExportStoragePermissionDialog thisImportExportStoragePermissionDialog = new ImportExportStoragePermissionDialog(); + thisImportExportStoragePermissionDialog.setArguments(argumentsBundle); + return thisImportExportStoragePermissionDialog; + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + // Store the download type in a local variable. + int type = getArguments().getInt("type"); + + // Use a builder to create the alert dialog. + AlertDialog.Builder dialogBuilder; + + // Set the style and the icon according to the theme. + if (MainWebViewActivity.darkTheme) { + dialogBuilder = new AlertDialog.Builder(getActivity(), R.style.PrivacyBrowserAlertDialogDark); + dialogBuilder.setIcon(R.drawable.import_export_dark); + } else { + dialogBuilder = new AlertDialog.Builder(getActivity(), R.style.PrivacyBrowserAlertDialogLight); + dialogBuilder.setIcon(R.drawable.import_export_light); + } + + // Set the title. + dialogBuilder.setTitle(R.string.storage_permission); + + // Set the text. + dialogBuilder.setMessage(R.string.storage_permission_message); + + // 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(type); + }); + + // Create an alert dialog from the builder. + final AlertDialog alertDialog = dialogBuilder.create(); + + // Disable screenshots if not allowed. + if (!MainWebViewActivity.allowScreenshots) { + // Remove the warning below that `getWindow()` might be null. + assert alertDialog.getWindow() != null; + + // Disable screenshots. + alertDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE); + } + + // `onCreateDialog()` requires the return of an `AlertDialog`. + return alertDialog; + } +} diff --git a/app/src/main/java/com/stoutner/privacybrowser/helpers/BookmarksDatabaseHelper.java b/app/src/main/java/com/stoutner/privacybrowser/helpers/BookmarksDatabaseHelper.java index e152b148..ac70f2c8 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/helpers/BookmarksDatabaseHelper.java +++ b/app/src/main/java/com/stoutner/privacybrowser/helpers/BookmarksDatabaseHelper.java @@ -1,5 +1,5 @@ /* - * Copyright © 2016-2017 Soren Stoutner . + * Copyright © 2016-2018 Soren Stoutner . * * This file is part of Privacy Browser . * @@ -28,8 +28,8 @@ import android.database.sqlite.SQLiteOpenHelper; public class BookmarksDatabaseHelper extends SQLiteOpenHelper { private static final int SCHEMA_VERSION = 1; - private static final String BOOKMARKS_DATABASE = "bookmarks.db"; - private static final String BOOKMARKS_TABLE = "bookmarks"; + static final String BOOKMARKS_DATABASE = "bookmarks.db"; + static final String BOOKMARKS_TABLE = "bookmarks"; public static final String _ID = "_id"; public static final String BOOKMARK_NAME = "bookmarkname"; @@ -39,6 +39,15 @@ public class BookmarksDatabaseHelper extends SQLiteOpenHelper { public static final String IS_FOLDER = "isfolder"; public static final String FAVORITE_ICON = "favoriteicon"; + static final String CREATE_BOOKMARKS_TABLE = "CREATE TABLE " + BOOKMARKS_TABLE + " (" + + _ID + " INTEGER PRIMARY KEY, " + + BOOKMARK_NAME + " TEXT, " + + BOOKMARK_URL + " TEXT, " + + PARENT_FOLDER + " TEXT, " + + DISPLAY_ORDER + " INTEGER, " + + IS_FOLDER + " BOOLEAN, " + + FAVORITE_ICON + " BLOB)"; + // Initialize the database. The lint warnings for the unused parameters are suppressed. public BookmarksDatabaseHelper(Context context, @SuppressWarnings("UnusedParameters") String name, SQLiteDatabase.CursorFactory cursorFactory, @SuppressWarnings("UnusedParameters") int version) { super(context, BOOKMARKS_DATABASE, cursorFactory, SCHEMA_VERSION); @@ -46,17 +55,7 @@ public class BookmarksDatabaseHelper extends SQLiteOpenHelper { @Override public void onCreate(SQLiteDatabase bookmarksDatabase) { - // Setup the SQL string to create the `bookmarks` table. - final String CREATE_BOOKMARKS_TABLE = "CREATE TABLE " + BOOKMARKS_TABLE + " (" + - _ID + " integer primary key, " + - BOOKMARK_NAME + " text, " + - BOOKMARK_URL + " text, " + - PARENT_FOLDER + " text, " + - DISPLAY_ORDER + " integer, " + - IS_FOLDER + " boolean, " + - FAVORITE_ICON + " blob);"; - - // Create the `bookmarks` table. + // Create the bookmarks table. bookmarksDatabase.execSQL(CREATE_BOOKMARKS_TABLE); } @@ -67,7 +66,7 @@ public class BookmarksDatabaseHelper extends SQLiteOpenHelper { // Create a bookmark. public void createBookmark(String bookmarkName, String bookmarkURL, String parentFolder, int displayOrder, byte[] favoriteIcon) { - // We need to store the bookmark data in a `ContentValues`. + // Store the bookmark data in a `ContentValues`. ContentValues bookmarkContentValues = new ContentValues(); // ID is created automatically. @@ -88,6 +87,18 @@ public class BookmarksDatabaseHelper extends SQLiteOpenHelper { bookmarksDatabase.close(); } + // Create a bookmark from content values. + void createBookmark(ContentValues contentValues) { + // Get a writable database. + SQLiteDatabase bookmarksDatabase = this.getWritableDatabase(); + + // Insert a new row. The second argument is `null`, which makes it so that a completely null row cannot be created. + bookmarksDatabase.insert(BOOKMARKS_TABLE, null, contentValues); + + // Close the database handle. + bookmarksDatabase.close(); + } + // Create a folder. public void createFolder(String folderName, String parentFolder, byte[] favoriteIcon) { ContentValues bookmarkContentValues = new ContentValues(); @@ -272,11 +283,10 @@ public class BookmarksDatabaseHelper extends SQLiteOpenHelper { // Get a readable database handle. SQLiteDatabase bookmarksDatabase = this.getReadableDatabase(); - // Get everything in `BOOKMARKS_TABLE`. + // Get everything in in the bookmarks table. final String GET_ALL_BOOKMARKS = "SELECT * FROM " + BOOKMARKS_TABLE; - // Return the results as a Cursor. The second argument is `null` because there are no selectionArgs. - // We can't close the Cursor because we need to use it in the parent activity. + // Return the results as a Cursor. The second argument is `null` because there are no selectionArgs. The Cursor cannot be closed because it is used in the parent activity. return bookmarksDatabase.rawQuery(GET_ALL_BOOKMARKS, null); } @@ -288,11 +298,11 @@ public class BookmarksDatabaseHelper extends SQLiteOpenHelper { // SQL escape `folderName`. folderName = DatabaseUtils.sqlEscapeString(folderName); - // Get everything in the `BOOKMARKS_TABLE` with `folderName` as the `PARENT_FOLDER`. + // Get everything in the bookmarks table with `folderName` as the `PARENT_FOLDER`. final String GET_ALL_BOOKMARKS = "SELECT * FROM " + BOOKMARKS_TABLE + " WHERE " + PARENT_FOLDER + " = " + folderName; - // Return the results as a `Cursor`. The second argument is `null` because there are no `selectionArgs`. We can't close the `Cursor` because we need to use it in the parent activity. + // Return the results as a `Cursor`. The second argument is `null` because there are no `selectionArgs`. The Cursor cannot be closed because it is used in the parent activity. return bookmarksDatabase.rawQuery(GET_ALL_BOOKMARKS, null); } @@ -309,7 +319,7 @@ public class BookmarksDatabaseHelper extends SQLiteOpenHelper { " WHERE " + PARENT_FOLDER + " = " + folderName + " ORDER BY " + DISPLAY_ORDER + " ASC"; - // Return the results as a `Cursor`. The second argument is `null` because there are no `selectionArgs`. We can't close the `Cursor` because we need to use it in the parent activity. + // Return the results as a `Cursor`. The second argument is `null` because there are no `selectionArgs`. The Cursor cannot be closed because it is used in the parent activity. return bookmarksDatabase.rawQuery(GET_ALL_BOOKMARKS, null); } diff --git a/app/src/main/java/com/stoutner/privacybrowser/helpers/DomainsDatabaseHelper.java b/app/src/main/java/com/stoutner/privacybrowser/helpers/DomainsDatabaseHelper.java index 0af0390a..2ed01cb4 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/helpers/DomainsDatabaseHelper.java +++ b/app/src/main/java/com/stoutner/privacybrowser/helpers/DomainsDatabaseHelper.java @@ -29,8 +29,8 @@ import android.preference.PreferenceManager; public class DomainsDatabaseHelper extends SQLiteOpenHelper { private static final int SCHEMA_VERSION = 8; - private static final String DOMAINS_DATABASE = "domains.db"; - private static final String DOMAINS_TABLE = "domains"; + static final String DOMAINS_DATABASE = "domains.db"; + static final String DOMAINS_TABLE = "domains"; public static final String _ID = "_id"; public static final String DOMAIN_NAME = "domainname"; @@ -75,6 +75,35 @@ public class DomainsDatabaseHelper extends SQLiteOpenHelper { public static final int DISPLAY_WEBPAGE_IMAGES_ENABLED = 1; public static final int DISPLAY_WEBPAGE_IMAGES_DISABLED = 2; + static final String CREATE_DOMAINS_TABLE = "CREATE TABLE " + DOMAINS_TABLE + " (" + + _ID + " INTEGER PRIMARY KEY, " + + DOMAIN_NAME + " TEXT, " + + ENABLE_JAVASCRIPT + " BOOLEAN, " + + ENABLE_FIRST_PARTY_COOKIES + " BOOLEAN, " + + ENABLE_THIRD_PARTY_COOKIES + " BOOLEAN, " + + ENABLE_DOM_STORAGE + " BOOLEAN, " + + ENABLE_FORM_DATA + " BOOLEAN, " + + ENABLE_EASYLIST + " BOOLEAN, " + + ENABLE_EASYPRIVACY + " BOOLEAN, " + + ENABLE_FANBOYS_ANNOYANCE_LIST + " BOOLEAN, " + + ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST + " BOOLEAN, " + + ENABLE_ULTRAPRIVACY + " BOOLEAN, " + + BLOCK_ALL_THIRD_PARTY_REQUESTS + " BOOLEAN, " + + USER_AGENT + " TEXT, " + + FONT_SIZE + " INTEGER, " + + SWIPE_TO_REFRESH + " INTEGER, " + + NIGHT_MODE + " INTEGER, " + + DISPLAY_IMAGES + " INTEGER, " + + PINNED_SSL_CERTIFICATE + " BOOLEAN, " + + SSL_ISSUED_TO_COMMON_NAME + " TEXT, " + + SSL_ISSUED_TO_ORGANIZATION + " TEXT, " + + SSL_ISSUED_TO_ORGANIZATIONAL_UNIT + " TEXT, " + + SSL_ISSUED_BY_COMMON_NAME + " TEXT, " + + SSL_ISSUED_BY_ORGANIZATION + " TEXT, " + + SSL_ISSUED_BY_ORGANIZATIONAL_UNIT + " TEXT, " + + SSL_START_DATE + " INTEGER, " + + SSL_END_DATE + " INTEGER)"; + private final Context appContext; // Initialize the database. The lint warnings for the unused parameters are suppressed. @@ -87,37 +116,7 @@ public class DomainsDatabaseHelper extends SQLiteOpenHelper { @Override public void onCreate(SQLiteDatabase domainsDatabase) { - // Setup the SQL string to create the `domains` table. - String CREATE_DOMAINS_TABLE = "CREATE TABLE " + DOMAINS_TABLE + " (" + - _ID + " INTEGER PRIMARY KEY, " + - DOMAIN_NAME + " TEXT, " + - ENABLE_JAVASCRIPT + " BOOLEAN, " + - ENABLE_FIRST_PARTY_COOKIES + " BOOLEAN, " + - ENABLE_THIRD_PARTY_COOKIES + " BOOLEAN, " + - ENABLE_DOM_STORAGE + " BOOLEAN, " + - ENABLE_FORM_DATA + " BOOLEAN, " + - ENABLE_EASYLIST + " BOOLEAN, " + - ENABLE_EASYPRIVACY + " BOOLEAN, " + - ENABLE_FANBOYS_ANNOYANCE_LIST + " BOOLEAN, " + - ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST + " BOOLEAN, " + - ENABLE_ULTRAPRIVACY + " BOOLEAN, " + - BLOCK_ALL_THIRD_PARTY_REQUESTS + " BOOLEAN, " + - USER_AGENT + " TEXT, " + - FONT_SIZE + " INTEGER, " + - SWIPE_TO_REFRESH + " INTEGER, " + - NIGHT_MODE + " INTEGER, " + - DISPLAY_IMAGES + " INTEGER, " + - PINNED_SSL_CERTIFICATE + " BOOLEAN, " + - SSL_ISSUED_TO_COMMON_NAME + " TEXT, " + - SSL_ISSUED_TO_ORGANIZATION + " TEXT, " + - SSL_ISSUED_TO_ORGANIZATIONAL_UNIT + " TEXT, " + - SSL_ISSUED_BY_COMMON_NAME + " TEXT, " + - SSL_ISSUED_BY_ORGANIZATION + " TEXT, " + - SSL_ISSUED_BY_ORGANIZATIONAL_UNIT + " TEXT, " + - SSL_START_DATE + " INTEGER, " + - SSL_END_DATE + " INTEGER);"; - - // Make it so. + // Create the domains table. domainsDatabase.execSQL(CREATE_DOMAINS_TABLE); } @@ -213,11 +212,19 @@ public class DomainsDatabaseHelper extends SQLiteOpenHelper { } } + Cursor getCompleteCursorOrderedByDomain() { + // Get a readable database handle. + SQLiteDatabase domainsDatabase = this.getReadableDatabase(); + + // Return everything in the domains table ordered by the domain name. The second argument is `null` because there are no `selectionArgs`. + return domainsDatabase.rawQuery("SELECT * FROM " + DOMAINS_TABLE + " ORDER BY " + DOMAIN_NAME + " ASC", null); + } + public Cursor getDomainNameCursorOrderedByDomain() { // Get a readable database handle. SQLiteDatabase domainsDatabase = this.getReadableDatabase(); - // Get everything in `DOMAINS_TABLE` ordered by `DOMAIN_NAME`. + // Get everything in the domains table ordered by the domain name. String GET_CURSOR_ORDERED_BY_DOMAIN = "SELECT " + _ID + ", " + DOMAIN_NAME + " FROM " + DOMAINS_TABLE + " ORDER BY " + DOMAIN_NAME + " ASC"; @@ -313,6 +320,17 @@ public class DomainsDatabaseHelper extends SQLiteOpenHelper { return newDomainDatabaseId; } + void addDomain(ContentValues contentValues) { + // Get a writable database handle. + SQLiteDatabase domainsDatabase = this.getWritableDatabase(); + + // Add the new domain. + domainsDatabase.insert(DOMAINS_TABLE, null, contentValues); + + // Close the database handle. + domainsDatabase.close(); + } + public void updateDomainExceptCertificate(int databaseId, String domainName, boolean javaScriptEnabled, boolean firstPartyCookiesEnabled, boolean thirdPartyCookiesEnabled, boolean domStorageEnabled, boolean formDataEnabled, boolean easyListEnabled, boolean easyPrivacyEnabled, boolean fanboysAnnoyanceEnabled, boolean fanboysSocialBlockingEnabled, boolean ultraPrivacyEnabled, boolean blockAllThirdPartyRequests, String userAgent, int fontSize, int swipeToRefresh, int nightMode, int displayImages, diff --git a/app/src/main/java/com/stoutner/privacybrowser/helpers/ImportExportDatabaseHelper.java b/app/src/main/java/com/stoutner/privacybrowser/helpers/ImportExportDatabaseHelper.java new file mode 100644 index 00000000..33887008 --- /dev/null +++ b/app/src/main/java/com/stoutner/privacybrowser/helpers/ImportExportDatabaseHelper.java @@ -0,0 +1,498 @@ +/* + * Copyright © 2018 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.helpers; + +import android.content.ContentValues; +import android.content.Context; +import android.content.SharedPreferences; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.preference.PreferenceManager; + +import java.io.File; + +public class ImportExportDatabaseHelper { + public static final String EXPORT_SUCCESSFUL = "Export Successful"; + public static final String IMPORT_SUCCESSFUL = "Import Successful"; + + private static final int SCHEMA_VERSION = 1; + private static final String PREFERENCES_TABLE = "preferences"; + + // The preferences constants. + private static final String _ID = "_id"; + private static final String JAVASCRIPT = "javascript"; + private static final String FIRST_PARTY_COOKIES = "first_party_cookies"; + private static final String THIRD_PARTY_COOKIES = "third_party_cookies"; + private static final String DOM_STORAGE = "dom_storage"; + private static final String SAVE_FORM_DATA = "save_form_data"; + private static final String USER_AGENT = "user_agent"; + private static final String CUSTOM_USER_AGENT = "custom_user_agent"; + private static final String INCOGNITO_MODE = "incognito_mode"; + private static final String DO_NOT_TRACK = "do_not_track"; + private static final String ALLOW_SCREENSHOTS = "allow_screenshots"; + private static final String EASYLIST = "easylist"; + private static final String EASYPRIVACY = "easyprivacy"; + private static final String FANBOYS_ANNOYANCE_LIST = "fanboys_annoyance_list"; + private static final String FANBOYS_SOCIAL_BLOCKING_LIST = "fanboys_social_blocking_list"; + private static final String ULTRAPRIVACY = "ultraprivacy"; + private static final String BLOCK_ALL_THIRD_PARTY_REQUESTS = "block_all_third_party_requests"; + private static final String PROXY_THROUGH_ORBOT = "proxy_through_orbot"; + private static final String TOR_HOMEPAGE = "tor_homepage"; + private static final String TOR_SEARCH = "tor_search"; + private static final String TOR_SEARCH_CUSTOM_URL = "tor_search_custom_url"; + private static final String SEARCH = "search"; + private static final String SEARCH_CUSTOM_URL = "search_custom_url"; + private static final String FULL_SCREEN_BROWSING_MODE = "full_screen_browsing_mode"; + private static final String HIDE_SYSTEM_BARS = "hide_system_bars"; + private static final String TRANSLUCENT_NAVIGATION_BAR = "translucent_navigation_bar"; + private static final String CLEAR_EVERYTHING = "clear_everything"; + private static final String CLEAR_COOKIES = "clear_cookies"; + private static final String CLEAR_DOM_STORAGE = "clear_dom_storage"; + private static final String CLEAR_FORM_DATA = "clear_form_data"; + private static final String CLEAR_CACHE = "clear_cache"; + private static final String HOMEPAGE = "homepage"; + private static final String DEFAULT_FONT_SIZE = "default_font_size"; + private static final String SWIPE_TO_REFRESH = "swipe_to_refresh"; + private static final String DISPLAY_ADDITIONAL_APP_BAR_ICONS = "display_additional_app_bar_icons"; + private static final String DARK_THEME = "dark_theme"; + private static final String NIGHT_MODE = "night_mode"; + private static final String DISPLAY_WEBPAGE_IMAGES = "display_webpage_images"; + + public String exportUnencrypted(File databaseFile, Context context) { + try { + // Delete the current file if it exists. + if (databaseFile.exists()) { + //noinspection ResultOfMethodCallIgnored + databaseFile.delete(); + } + + // Create the export database. + SQLiteDatabase exportDatabase = SQLiteDatabase.openOrCreateDatabase(databaseFile, null); + + // Set the export database version number. + exportDatabase.setVersion(SCHEMA_VERSION); + + // Create the export database domains table. + exportDatabase.execSQL(DomainsDatabaseHelper.CREATE_DOMAINS_TABLE); + + // Open the domains database. The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`. + DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(context, null, null, 0); + + // Get a full domains database cursor. + Cursor domainsCursor = domainsDatabaseHelper.getCompleteCursorOrderedByDomain(); + + // Move to the first domain. + domainsCursor.moveToFirst(); + + // Copy the data from the domains cursor into the export database. + for (int i = 0; i < domainsCursor.getCount(); i++) { + // Extract the record from the cursor and store the data in a ContentValues. + ContentValues domainsContentValues = new ContentValues(); + domainsContentValues.put(DomainsDatabaseHelper.DOMAIN_NAME, domainsCursor.getString(domainsCursor.getColumnIndex(DomainsDatabaseHelper.DOMAIN_NAME))); + domainsContentValues.put(DomainsDatabaseHelper.ENABLE_JAVASCRIPT, domainsCursor.getInt(domainsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_JAVASCRIPT))); + domainsContentValues.put(DomainsDatabaseHelper.ENABLE_FIRST_PARTY_COOKIES, domainsCursor.getInt(domainsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FIRST_PARTY_COOKIES))); + domainsContentValues.put(DomainsDatabaseHelper.ENABLE_THIRD_PARTY_COOKIES, domainsCursor.getInt(domainsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_THIRD_PARTY_COOKIES))); + domainsContentValues.put(DomainsDatabaseHelper.ENABLE_DOM_STORAGE, domainsCursor.getInt(domainsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_DOM_STORAGE))); + domainsContentValues.put(DomainsDatabaseHelper.ENABLE_FORM_DATA, domainsCursor.getInt(domainsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FORM_DATA))); + domainsContentValues.put(DomainsDatabaseHelper.ENABLE_EASYLIST, domainsCursor.getInt(domainsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_EASYLIST))); + domainsContentValues.put(DomainsDatabaseHelper.ENABLE_EASYPRIVACY, domainsCursor.getInt(domainsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_EASYPRIVACY))); + domainsContentValues.put(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST, domainsCursor.getInt(domainsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST))); + domainsContentValues.put(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST, domainsCursor.getInt(domainsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST))); + domainsContentValues.put(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY, domainsCursor.getInt(domainsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY))); + domainsContentValues.put(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS, domainsCursor.getInt(domainsCursor.getColumnIndex(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS))); + domainsContentValues.put(DomainsDatabaseHelper.USER_AGENT, domainsCursor.getString(domainsCursor.getColumnIndex(DomainsDatabaseHelper.USER_AGENT))); + domainsContentValues.put(DomainsDatabaseHelper.FONT_SIZE, domainsCursor.getInt(domainsCursor.getColumnIndex(DomainsDatabaseHelper.FONT_SIZE))); + domainsContentValues.put(DomainsDatabaseHelper.SWIPE_TO_REFRESH, domainsCursor.getInt(domainsCursor.getColumnIndex(DomainsDatabaseHelper.SWIPE_TO_REFRESH))); + domainsContentValues.put(DomainsDatabaseHelper.NIGHT_MODE, domainsCursor.getInt(domainsCursor.getColumnIndex(DomainsDatabaseHelper.NIGHT_MODE))); + domainsContentValues.put(DomainsDatabaseHelper.DISPLAY_IMAGES, domainsCursor.getInt(domainsCursor.getColumnIndex(DomainsDatabaseHelper.DISPLAY_IMAGES))); + domainsContentValues.put(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE, domainsCursor.getInt(domainsCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE))); + domainsContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME, domainsCursor.getString(domainsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME))); + domainsContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION, domainsCursor.getString(domainsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION))); + domainsContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT, domainsCursor.getString(domainsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT))); + domainsContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME, domainsCursor.getString(domainsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME))); + domainsContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION, domainsCursor.getString(domainsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION))); + domainsContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT, domainsCursor.getString(domainsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT))); + domainsContentValues.put(DomainsDatabaseHelper.SSL_START_DATE, domainsCursor.getLong(domainsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE))); + domainsContentValues.put(DomainsDatabaseHelper.SSL_END_DATE, domainsCursor.getLong(domainsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE))); + + // Insert the record into the export database. + exportDatabase.insert(DomainsDatabaseHelper.DOMAINS_TABLE, null, domainsContentValues); + + // Advance to the next record. + domainsCursor.moveToNext(); + } + + // Close the domains database. + domainsCursor.close(); + domainsDatabaseHelper.close(); + + + // Create the export database bookmarks table. + exportDatabase.execSQL(BookmarksDatabaseHelper.CREATE_BOOKMARKS_TABLE); + + // Open the bookmarks database. The `0` specifies the database version, but that is ignored and set instead using a constant in `BookmarksDatabaseHelper`. + BookmarksDatabaseHelper bookmarksDatabaseHelper = new BookmarksDatabaseHelper(context, null, null, 0); + + // Get a full bookmarks cursor. + Cursor bookmarksCursor = bookmarksDatabaseHelper.getAllBookmarksCursor(); + + // Move to the first bookmark. + bookmarksCursor.moveToFirst(); + + // Copy the data from the bookmarks cursor into the export database. + for (int i = 0; i < bookmarksCursor.getCount(); i++) { + // Extract the record from the cursor and store the data in a ContentValues. + ContentValues bookmarksContentValues = new ContentValues(); + bookmarksContentValues.put(BookmarksDatabaseHelper.BOOKMARK_NAME, bookmarksCursor.getString(bookmarksCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME))); + bookmarksContentValues.put(BookmarksDatabaseHelper.BOOKMARK_URL, bookmarksCursor.getString(bookmarksCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_URL))); + bookmarksContentValues.put(BookmarksDatabaseHelper.PARENT_FOLDER, bookmarksCursor.getString(bookmarksCursor.getColumnIndex(BookmarksDatabaseHelper.PARENT_FOLDER))); + bookmarksContentValues.put(BookmarksDatabaseHelper.DISPLAY_ORDER, bookmarksCursor.getInt(bookmarksCursor.getColumnIndex(BookmarksDatabaseHelper.DISPLAY_ORDER))); + bookmarksContentValues.put(BookmarksDatabaseHelper.IS_FOLDER, bookmarksCursor.getInt(bookmarksCursor.getColumnIndex(BookmarksDatabaseHelper.IS_FOLDER))); + bookmarksContentValues.put(BookmarksDatabaseHelper.FAVORITE_ICON, bookmarksCursor.getBlob(bookmarksCursor.getColumnIndex(BookmarksDatabaseHelper.FAVORITE_ICON))); + + // Insert the record into the export database. + exportDatabase.insert(BookmarksDatabaseHelper.BOOKMARKS_TABLE, null, bookmarksContentValues); + + // Advance to the next record. + bookmarksCursor.moveToNext(); + } + + // Close the bookmarks database. + bookmarksCursor.close(); + bookmarksDatabaseHelper.close(); + + + // Prepare the preferences table SQL creation string. + String CREATE_PREFERENCES_TABLE = "CREATE TABLE " + PREFERENCES_TABLE + " (" + + _ID + " INTEGER PRIMARY KEY, " + + JAVASCRIPT + " BOOLEAN, " + + FIRST_PARTY_COOKIES + " BOOLEAN, " + + THIRD_PARTY_COOKIES + " BOOLEAN, " + + DOM_STORAGE + " BOOLEAN, " + + SAVE_FORM_DATA + " BOOLEAN, " + + USER_AGENT + " TEXT, " + + CUSTOM_USER_AGENT + " TEXT, " + + INCOGNITO_MODE + " BOOLEAN, " + + DO_NOT_TRACK + " BOOLEAN, " + + ALLOW_SCREENSHOTS + " BOOLEAN, " + + EASYLIST + " BOOLEAN, " + + EASYPRIVACY + " BOOLEAN, " + + FANBOYS_ANNOYANCE_LIST + " BOOLEAN, " + + FANBOYS_SOCIAL_BLOCKING_LIST + " BOOLEAN, " + + ULTRAPRIVACY + " BOOLEAN, " + + BLOCK_ALL_THIRD_PARTY_REQUESTS + " BOOLEAN, " + + PROXY_THROUGH_ORBOT + " BOOLEAN, " + + TOR_HOMEPAGE + " TEXT, " + + TOR_SEARCH + " TEXT, " + + TOR_SEARCH_CUSTOM_URL + " TEXT, " + + SEARCH + " TEXT, " + + SEARCH_CUSTOM_URL + " TEXT, " + + FULL_SCREEN_BROWSING_MODE + " BOOLEAN, " + + HIDE_SYSTEM_BARS + " BOOLEAN, " + + TRANSLUCENT_NAVIGATION_BAR + " BOOLEAN, " + + CLEAR_EVERYTHING + " BOOLEAN, " + + CLEAR_COOKIES + " BOOLEAN, " + + CLEAR_DOM_STORAGE + " BOOLEAN, " + + CLEAR_FORM_DATA + " BOOLEAN, " + + CLEAR_CACHE + " BOOLEAN, " + + HOMEPAGE + " TEXT, " + + DEFAULT_FONT_SIZE + " TEXT, " + + SWIPE_TO_REFRESH + " BOOLEAN, " + + DISPLAY_ADDITIONAL_APP_BAR_ICONS + " BOOLEAN, " + + DARK_THEME + " BOOLEAN, " + + NIGHT_MODE + " BOOLEAN, " + + DISPLAY_WEBPAGE_IMAGES + " BOOLEAN)"; + + // Create the export database preferences table. + exportDatabase.execSQL(CREATE_PREFERENCES_TABLE); + + // Get a handle for the shared preference. + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); + + // Create a ContentValues with the preferences information. + ContentValues preferencesContentValues = new ContentValues(); + preferencesContentValues.put(JAVASCRIPT, sharedPreferences.getBoolean("javascript_enabled", false)); + preferencesContentValues.put(FIRST_PARTY_COOKIES, sharedPreferences.getBoolean("first_party_cookies_enabled", false)); + preferencesContentValues.put(THIRD_PARTY_COOKIES, sharedPreferences.getBoolean("third_party_cookies_enabled", false)); + preferencesContentValues.put(DOM_STORAGE, sharedPreferences.getBoolean("dom_storage_enabled", false)); + preferencesContentValues.put(SAVE_FORM_DATA, sharedPreferences.getBoolean("save_form_data_enabled", false)); // Save form data can be removed once the minimum API >= 26. + preferencesContentValues.put(USER_AGENT, sharedPreferences.getString("user_agent", "Privacy Browser")); + preferencesContentValues.put(CUSTOM_USER_AGENT, sharedPreferences.getString("custom_user_agent", "PrivacyBrowser/1.0")); + preferencesContentValues.put(INCOGNITO_MODE, sharedPreferences.getBoolean("incognito_mode", false)); + preferencesContentValues.put(DO_NOT_TRACK, sharedPreferences.getBoolean("do_not_track", false)); + preferencesContentValues.put(ALLOW_SCREENSHOTS, sharedPreferences.getBoolean("allow_screenshots", false)); + preferencesContentValues.put(EASYLIST, sharedPreferences.getBoolean("easylist", true)); + preferencesContentValues.put(EASYPRIVACY, sharedPreferences.getBoolean("easyprivacy", true)); + preferencesContentValues.put(FANBOYS_ANNOYANCE_LIST, sharedPreferences.getBoolean("fanboy_annoyance_list", true)); + preferencesContentValues.put(FANBOYS_SOCIAL_BLOCKING_LIST, sharedPreferences.getBoolean("fanboy_social_blocking_list", true)); + preferencesContentValues.put(ULTRAPRIVACY, sharedPreferences.getBoolean("ultraprivacy", true)); + preferencesContentValues.put(BLOCK_ALL_THIRD_PARTY_REQUESTS, sharedPreferences.getBoolean("block_all_third_party_requests", false)); + preferencesContentValues.put(PROXY_THROUGH_ORBOT, sharedPreferences.getBoolean("proxy_through_orbot", false)); + preferencesContentValues.put(TOR_HOMEPAGE, sharedPreferences.getString("tor_homepage", "http://ulrn6sryqaifefld.onion/")); + preferencesContentValues.put(TOR_SEARCH, sharedPreferences.getString("tor_search", "http://ulrn6sryqaifefld.onion/?q=")); + preferencesContentValues.put(TOR_SEARCH_CUSTOM_URL, sharedPreferences.getString("tor_search_custom_url", "")); + preferencesContentValues.put(SEARCH, sharedPreferences.getString("search", "https://searx.me/?q=")); + preferencesContentValues.put(SEARCH_CUSTOM_URL, sharedPreferences.getString("search_custom_url", "")); + preferencesContentValues.put(FULL_SCREEN_BROWSING_MODE, sharedPreferences.getBoolean("full_screen_browsing_mode", false)); + preferencesContentValues.put(HIDE_SYSTEM_BARS, sharedPreferences.getBoolean("hide_system_bars", false)); + preferencesContentValues.put(TRANSLUCENT_NAVIGATION_BAR, sharedPreferences.getBoolean("translucent_navigation_bar", true)); + preferencesContentValues.put(CLEAR_EVERYTHING, sharedPreferences.getBoolean("clear_everything", true)); + preferencesContentValues.put(CLEAR_COOKIES, sharedPreferences.getBoolean("clear_cookies", true)); + preferencesContentValues.put(CLEAR_DOM_STORAGE, sharedPreferences.getBoolean("clear_dom_storage", true)); + preferencesContentValues.put(CLEAR_FORM_DATA, sharedPreferences.getBoolean("clear_form_data", true)); // Clear form data can be removed once the minimum API >= 26. + preferencesContentValues.put(CLEAR_CACHE, sharedPreferences.getBoolean("clear_cache", true)); + preferencesContentValues.put(HOMEPAGE, sharedPreferences.getString("homepage", "https://searx.me")); + preferencesContentValues.put(DEFAULT_FONT_SIZE, sharedPreferences.getString("default_font_size", "100")); + preferencesContentValues.put(SWIPE_TO_REFRESH, sharedPreferences.getBoolean("swipe_to_refresh", true)); + preferencesContentValues.put(DISPLAY_ADDITIONAL_APP_BAR_ICONS, sharedPreferences.getBoolean("display_additional_app_bar_icons", false)); + preferencesContentValues.put(DARK_THEME, sharedPreferences.getBoolean("dark_theme", false)); + preferencesContentValues.put(NIGHT_MODE, sharedPreferences.getBoolean("night_mode", false)); + preferencesContentValues.put(DISPLAY_WEBPAGE_IMAGES, sharedPreferences.getBoolean("display_webpage_images", true)); + + // Insert the preferences into the export database. + exportDatabase.insert(PREFERENCES_TABLE, null, preferencesContentValues); + + // Close the export database. + exportDatabase.close(); + + // Convert the database file to a string. + String databaseString = databaseFile.toString(); + + // Create strings for the temporary database files. + String journalFileString = databaseString + "-journal"; + + // Get `Files` for the temporary database files. + File journalFile = new File(journalFileString); + + // Delete the Journal file if it exists. + if (journalFile.exists()) { + //noinspection ResultOfMethodCallIgnored + journalFile.delete(); + } + + // Export successful. + return EXPORT_SUCCESSFUL; + } catch (Exception exception) { + // Return the export error. + return exception.toString(); + } + } + + public String importUnencrypted(File databaseFile, Context context){ + try { + // Convert the database file to a string. Once API >= 27 the file can be opened directly. + String databaseString = databaseFile.toString(); + + // Open the import database. + SQLiteDatabase importDatabase = SQLiteDatabase.openDatabase(databaseString, null, SQLiteDatabase.OPEN_READONLY); + + // Get a cursor for the domains table. + Cursor importDomainsCursor = importDatabase.rawQuery("SELECT * FROM " + DomainsDatabaseHelper.DOMAINS_TABLE + " ORDER BY " + DomainsDatabaseHelper.DOMAIN_NAME + " ASC", null); + + // Delete the current domains database. + context.deleteDatabase(DomainsDatabaseHelper.DOMAINS_DATABASE); + + // Create a new domains database. The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`. + DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(context, null, null, 0); + + // Move to the first domain. + importDomainsCursor.moveToFirst(); + + // Copy the data from the import domains cursor into the domains database. + for (int i = 0; i < importDomainsCursor.getCount(); i++) { + // Extract the record from the cursor and store the data in a ContentValues. + ContentValues domainsContentValues = new ContentValues(); + domainsContentValues.put(DomainsDatabaseHelper.DOMAIN_NAME, importDomainsCursor.getString(importDomainsCursor.getColumnIndex(DomainsDatabaseHelper.DOMAIN_NAME))); + domainsContentValues.put(DomainsDatabaseHelper.ENABLE_JAVASCRIPT, importDomainsCursor.getInt(importDomainsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_JAVASCRIPT))); + domainsContentValues.put(DomainsDatabaseHelper.ENABLE_FIRST_PARTY_COOKIES, importDomainsCursor.getInt(importDomainsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FIRST_PARTY_COOKIES))); + domainsContentValues.put(DomainsDatabaseHelper.ENABLE_THIRD_PARTY_COOKIES, importDomainsCursor.getInt(importDomainsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_THIRD_PARTY_COOKIES))); + domainsContentValues.put(DomainsDatabaseHelper.ENABLE_DOM_STORAGE, importDomainsCursor.getInt(importDomainsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_DOM_STORAGE))); + domainsContentValues.put(DomainsDatabaseHelper.ENABLE_FORM_DATA, importDomainsCursor.getInt(importDomainsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FORM_DATA))); + domainsContentValues.put(DomainsDatabaseHelper.ENABLE_EASYLIST, importDomainsCursor.getInt(importDomainsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_EASYLIST))); + domainsContentValues.put(DomainsDatabaseHelper.ENABLE_EASYPRIVACY, importDomainsCursor.getInt(importDomainsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_EASYPRIVACY))); + domainsContentValues.put(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST, + importDomainsCursor.getInt(importDomainsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST))); + domainsContentValues.put(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST, + importDomainsCursor.getInt(importDomainsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST))); + domainsContentValues.put(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY, importDomainsCursor.getInt(importDomainsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY))); + domainsContentValues.put(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS, + importDomainsCursor.getInt(importDomainsCursor.getColumnIndex(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS))); + domainsContentValues.put(DomainsDatabaseHelper.USER_AGENT, importDomainsCursor.getString(importDomainsCursor.getColumnIndex(DomainsDatabaseHelper.USER_AGENT))); + domainsContentValues.put(DomainsDatabaseHelper.FONT_SIZE, importDomainsCursor.getInt(importDomainsCursor.getColumnIndex(DomainsDatabaseHelper.FONT_SIZE))); + domainsContentValues.put(DomainsDatabaseHelper.SWIPE_TO_REFRESH, importDomainsCursor.getInt(importDomainsCursor.getColumnIndex(DomainsDatabaseHelper.SWIPE_TO_REFRESH))); + domainsContentValues.put(DomainsDatabaseHelper.NIGHT_MODE, importDomainsCursor.getInt(importDomainsCursor.getColumnIndex(DomainsDatabaseHelper.NIGHT_MODE))); + domainsContentValues.put(DomainsDatabaseHelper.DISPLAY_IMAGES, importDomainsCursor.getInt(importDomainsCursor.getColumnIndex(DomainsDatabaseHelper.DISPLAY_IMAGES))); + domainsContentValues.put(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE, importDomainsCursor.getInt(importDomainsCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE))); + domainsContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME, importDomainsCursor.getString(importDomainsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME))); + domainsContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION, importDomainsCursor.getString(importDomainsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION))); + domainsContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT, + importDomainsCursor.getString(importDomainsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT))); + domainsContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME, importDomainsCursor.getString(importDomainsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME))); + domainsContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION, importDomainsCursor.getString(importDomainsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION))); + domainsContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT, + importDomainsCursor.getString(importDomainsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT))); + domainsContentValues.put(DomainsDatabaseHelper.SSL_START_DATE, importDomainsCursor.getLong(importDomainsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE))); + domainsContentValues.put(DomainsDatabaseHelper.SSL_END_DATE, importDomainsCursor.getLong(importDomainsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE))); + + // Insert the record into the export database. + domainsDatabaseHelper.addDomain(domainsContentValues); + + // Advance to the next record. + importDomainsCursor.moveToNext(); + } + + // Close the domains cursor. + importDomainsCursor.close(); + + // Close the domains database. + domainsDatabaseHelper.close(); + + + // Get a cursor for the bookmarks table. + Cursor importBookmarksCursor = importDatabase.rawQuery("SELECT * FROM " + BookmarksDatabaseHelper.BOOKMARKS_TABLE, null); + + // Delete the current bookmarks database. + context.deleteDatabase(BookmarksDatabaseHelper.BOOKMARKS_DATABASE); + + // Create a new bookmarks database. The `0` specifies the database version, but that is ignored and set instead using a constant in `BookmarksDatabaseHelper`. + BookmarksDatabaseHelper bookmarksDatabaseHelper = new BookmarksDatabaseHelper(context, null, null, 0); + + // Move to the first bookmark. + importBookmarksCursor.moveToFirst(); + + // Copy the data from the import bookmarks cursor into the bookmarks database. + for (int i = 0; i < importBookmarksCursor.getCount(); i++) { + // Extract the record from the cursor and store the data in a ContentValues. + ContentValues bookmarksContentValues = new ContentValues(); + bookmarksContentValues.put(BookmarksDatabaseHelper.BOOKMARK_NAME, importBookmarksCursor.getString(importBookmarksCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME))); + bookmarksContentValues.put(BookmarksDatabaseHelper.BOOKMARK_URL, importBookmarksCursor.getString(importBookmarksCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_URL))); + bookmarksContentValues.put(BookmarksDatabaseHelper.PARENT_FOLDER, importBookmarksCursor.getString(importBookmarksCursor.getColumnIndex(BookmarksDatabaseHelper.PARENT_FOLDER))); + bookmarksContentValues.put(BookmarksDatabaseHelper.DISPLAY_ORDER, importBookmarksCursor.getInt(importBookmarksCursor.getColumnIndex(BookmarksDatabaseHelper.DISPLAY_ORDER))); + bookmarksContentValues.put(BookmarksDatabaseHelper.IS_FOLDER, importBookmarksCursor.getInt(importBookmarksCursor.getColumnIndex(BookmarksDatabaseHelper.IS_FOLDER))); + bookmarksContentValues.put(BookmarksDatabaseHelper.FAVORITE_ICON, importBookmarksCursor.getBlob(importBookmarksCursor.getColumnIndex(BookmarksDatabaseHelper.FAVORITE_ICON))); + + // Insert the record into the export database. + bookmarksDatabaseHelper.createBookmark(bookmarksContentValues); + + // Advance to the next record. + importBookmarksCursor.moveToNext(); + } + + // Close the bookmarks cursor. + importBookmarksCursor.close(); + + // Close the bookmarks database. + bookmarksDatabaseHelper.close(); + + + // Get a handle for the shared preference. + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); + + // Get a cursor for the bookmarks table. + Cursor importPreferencesCursor = importDatabase.rawQuery("SELECT * FROM " + PREFERENCES_TABLE, null); + + // Move to the first preference. + importPreferencesCursor.moveToFirst(); + + // Import the preference data. + sharedPreferences.edit() + .putBoolean("javascript_enabled", importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndex(JAVASCRIPT)) == 1) + .putBoolean("first_party_cookies_enabled", importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndex(FIRST_PARTY_COOKIES)) == 1) + .putBoolean("third_party_cookies_enabled", importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndex(THIRD_PARTY_COOKIES)) == 1) + .putBoolean("dom_storage_enabled", importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndex(DOM_STORAGE)) == 1) + // Save form data can be removed once the minimum API >= 26. + .putBoolean("save_form_data_enabled", importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndex(SAVE_FORM_DATA)) == 1) + .putString("user_agent", importPreferencesCursor.getString(importPreferencesCursor.getColumnIndex(USER_AGENT))) + .putString("custom_user_agent", importPreferencesCursor.getString(importPreferencesCursor.getColumnIndex(CUSTOM_USER_AGENT))) + .putBoolean("incognito_mode", importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndex(INCOGNITO_MODE)) == 1) + .putBoolean("do_not_track", importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndex(DO_NOT_TRACK)) == 1) + .putBoolean("allow_screenshots", importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndex(ALLOW_SCREENSHOTS)) == 1) + .putBoolean("easylist", importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndex(EASYLIST)) == 1) + .putBoolean("easyprivacy", importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndex(EASYPRIVACY)) == 1) + .putBoolean("fanboy_annoyance_list", importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndex(FANBOYS_ANNOYANCE_LIST)) == 1) + .putBoolean("fanboy_social_blocking_list", importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndex(FANBOYS_SOCIAL_BLOCKING_LIST)) == 1) + .putBoolean("ultraprivacy", importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndex(ULTRAPRIVACY)) == 1) + .putBoolean("block_all_third_party_requests", importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndex(BLOCK_ALL_THIRD_PARTY_REQUESTS)) == 1) + .putBoolean("proxy_through_orbot", importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndex(PROXY_THROUGH_ORBOT)) == 1) + .putString("tor_homepage", importPreferencesCursor.getString(importPreferencesCursor.getColumnIndex(TOR_HOMEPAGE))) + .putString("tor_search", importPreferencesCursor.getString(importPreferencesCursor.getColumnIndex(TOR_SEARCH))) + .putString("tor_search_custom_url", importPreferencesCursor.getString(importPreferencesCursor.getColumnIndex(TOR_SEARCH_CUSTOM_URL))) + .putString("search", importPreferencesCursor.getString(importPreferencesCursor.getColumnIndex(SEARCH))) + .putString("search_custom_url", importPreferencesCursor.getString(importPreferencesCursor.getColumnIndex(SEARCH_CUSTOM_URL))) + .putBoolean("full_screen_browsing_mode", importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndex(FULL_SCREEN_BROWSING_MODE)) == 1) + .putBoolean("hide_system_bars", importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndex(HIDE_SYSTEM_BARS)) == 1) + .putBoolean("translucent_navigation_bar", importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndex(TRANSLUCENT_NAVIGATION_BAR)) == 1) + .putBoolean("clear_everything", importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndex(CLEAR_EVERYTHING)) == 1) + .putBoolean("clear_cookies", importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndex(CLEAR_COOKIES)) == 1) + .putBoolean("clear_dom_storage", importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndex(CLEAR_DOM_STORAGE)) == 1) + // Clear form data can be removed once the minimum API >= 26. + .putBoolean("clear_form_data", importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndex(CLEAR_FORM_DATA)) == 1) + .putBoolean("clear_cache", importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndex(CLEAR_CACHE)) == 1) + .putString("homepage", importPreferencesCursor.getString(importPreferencesCursor.getColumnIndex(HOMEPAGE))) + .putString("default_font_size", importPreferencesCursor.getString(importPreferencesCursor.getColumnIndex(DEFAULT_FONT_SIZE))) + .putBoolean("swipe_to_refresh", importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndex(SWIPE_TO_REFRESH)) == 1) + .putBoolean("display_additional_app_bar_icons", importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndex(DISPLAY_ADDITIONAL_APP_BAR_ICONS)) == 1) + .putBoolean("dark_theme", importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndex(DARK_THEME)) == 1) + .putBoolean("night_mode", importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndex(NIGHT_MODE)) == 1) + .putBoolean("display_webpage_images", importPreferencesCursor.getInt(importPreferencesCursor.getColumnIndex(DISPLAY_WEBPAGE_IMAGES)) == 1).apply(); + + // Close the preferences cursor. + importPreferencesCursor.close(); + + + // Close the import database. + importDatabase.close(); + + // Create strings for the temporary database files. + String shmFileString = databaseString + "-shm"; + String walFileString = databaseString + "-wal"; + String journalFileString = databaseString + "-journal"; + + // Get `Files` for the temporary database files. + File shmFile = new File(shmFileString); + File walFile = new File(walFileString); + File journalFile = new File(journalFileString); + + // Delete the Shared Memory file if it exists. + if (shmFile.exists()) { + //noinspection ResultOfMethodCallIgnored + shmFile.delete(); + } + + // Delete the Write Ahead Log file if it exists. + if (walFile.exists()) { + //noinspection ResultOfMethodCallIgnored + walFile.delete(); + } + + // Delete the Journal file if it exists. + if (journalFile.exists()) { + //noinspection ResultOfMethodCallIgnored + journalFile.delete(); + } + + // Import successful. + return IMPORT_SUCCESSFUL; + } catch (Exception exception) { + // Return the import error. + return exception.toString(); + } + } +} diff --git a/app/src/main/res/drawable/images_disabled_dark.xml b/app/src/main/res/drawable/images_disabled_dark.xml index f22f38a9..d7947b1e 100644 --- a/app/src/main/res/drawable/images_disabled_dark.xml +++ b/app/src/main/res/drawable/images_disabled_dark.xml @@ -4,15 +4,15 @@ - + + android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z" /> diff --git a/app/src/main/res/drawable/images_disabled_light.xml b/app/src/main/res/drawable/images_disabled_light.xml index e1baa4c4..95f5b1a6 100644 --- a/app/src/main/res/drawable/images_disabled_light.xml +++ b/app/src/main/res/drawable/images_disabled_light.xml @@ -4,15 +4,15 @@ - + + android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z" /> diff --git a/app/src/main/res/drawable/images_enabled_dark.xml b/app/src/main/res/drawable/images_enabled_dark.xml index 9167e1e9..cb45f476 100644 --- a/app/src/main/res/drawable/images_enabled_dark.xml +++ b/app/src/main/res/drawable/images_enabled_dark.xml @@ -4,15 +4,15 @@ - + + android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z" /> diff --git a/app/src/main/res/drawable/images_enabled_light.xml b/app/src/main/res/drawable/images_enabled_light.xml index 94f0b289..6241eb9f 100644 --- a/app/src/main/res/drawable/images_enabled_light.xml +++ b/app/src/main/res/drawable/images_enabled_light.xml @@ -4,15 +4,15 @@ - + + android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z" /> diff --git a/app/src/main/res/drawable/import_export_dark.xml b/app/src/main/res/drawable/import_export_dark.xml new file mode 100644 index 00000000..21cdea86 --- /dev/null +++ b/app/src/main/res/drawable/import_export_dark.xml @@ -0,0 +1,18 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/import_export_light.xml b/app/src/main/res/drawable/import_export_light.xml new file mode 100644 index 00000000..602cf376 --- /dev/null +++ b/app/src/main/res/drawable/import_export_light.xml @@ -0,0 +1,18 @@ + + + + + + + + diff --git a/app/src/main/res/layout/import_export_coordinatorlayout.xml b/app/src/main/res/layout/import_export_coordinatorlayout.xml new file mode 100644 index 00000000..76464fef --- /dev/null +++ b/app/src/main/res/layout/import_export_coordinatorlayout.xml @@ -0,0 +1,210 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +