From 91154b307513e7bc6958b27fba518e4f9b564cf9 Mon Sep 17 00:00:00 2001 From: Soren Stoutner Date: Tue, 19 Jan 2021 18:07:22 -0700 Subject: [PATCH] Convert a number of files to Kotlin. https://redmine.stoutner.com/issues/641 --- .../activities/AboutActivity.java | 5 +- .../activities/BookmarksActivity.java | 17 +- .../activities/MainWebViewActivity.java | 15 +- .../adapters/AboutPagerAdapter.java | 107 ---- .../adapters/AboutPagerAdapter.kt | 79 +++ .../adapters/PinnedMismatchPagerAdapter.kt | 302 ++++++++++ .../dialogs/AboutViewSourceDialog.kt | 4 +- .../privacybrowser/dialogs/AddDomainDialog.kt | 16 +- .../dialogs/CreateBookmarkDialog.kt | 12 +- .../dialogs/CreateBookmarkFolderDialog.kt | 12 +- .../dialogs/CreateHomeScreenShortcutDialog.kt | 6 +- .../dialogs/EditBookmarkDatabaseViewDialog.kt | 16 +- .../dialogs/EditBookmarkDialog.kt | 16 +- .../EditBookmarkFolderDatabaseViewDialog.kt | 16 +- .../dialogs/EditBookmarkFolderDialog.kt | 16 +- .../privacybrowser/dialogs/FontSizeDialog.kt | 12 +- .../dialogs/HttpAuthenticationDialog.kt | 6 +- .../dialogs/MoveToFolderDialog.java | 322 ----------- .../dialogs/MoveToFolderDialog.kt | 325 +++++++++++ .../privacybrowser/dialogs/OpenDialog.java | 218 -------- .../privacybrowser/dialogs/OpenDialog.kt | 188 +++++++ .../dialogs/PinnedMismatchDialog.java | 525 ------------------ .../dialogs/PinnedMismatchDialog.kt | 234 ++++++++ .../dialogs/ProxyNotInstalledDialog.java | 133 ----- .../dialogs/ProxyNotInstalledDialog.kt | 115 ++++ .../privacybrowser/dialogs/SaveDialog.java | 299 ---------- .../privacybrowser/dialogs/SaveDialog.kt | 256 +++++++++ .../fragments/AboutWebViewFragment.java | 193 ------- .../fragments/AboutWebViewFragment.kt | 153 +++++ ...l => pinned_mismatch_tab_linearlayout.xml} | 2 +- .../privacybrowser/helpers/AdHelper.java | 7 +- build.gradle | 2 +- 32 files changed, 1740 insertions(+), 1889 deletions(-) delete mode 100644 app/src/main/java/com/stoutner/privacybrowser/adapters/AboutPagerAdapter.java create mode 100644 app/src/main/java/com/stoutner/privacybrowser/adapters/AboutPagerAdapter.kt create mode 100644 app/src/main/java/com/stoutner/privacybrowser/adapters/PinnedMismatchPagerAdapter.kt delete mode 100644 app/src/main/java/com/stoutner/privacybrowser/dialogs/MoveToFolderDialog.java create mode 100644 app/src/main/java/com/stoutner/privacybrowser/dialogs/MoveToFolderDialog.kt delete mode 100644 app/src/main/java/com/stoutner/privacybrowser/dialogs/OpenDialog.java create mode 100644 app/src/main/java/com/stoutner/privacybrowser/dialogs/OpenDialog.kt delete mode 100644 app/src/main/java/com/stoutner/privacybrowser/dialogs/PinnedMismatchDialog.java create mode 100644 app/src/main/java/com/stoutner/privacybrowser/dialogs/PinnedMismatchDialog.kt delete mode 100644 app/src/main/java/com/stoutner/privacybrowser/dialogs/ProxyNotInstalledDialog.java create mode 100644 app/src/main/java/com/stoutner/privacybrowser/dialogs/ProxyNotInstalledDialog.kt delete mode 100644 app/src/main/java/com/stoutner/privacybrowser/dialogs/SaveDialog.java create mode 100644 app/src/main/java/com/stoutner/privacybrowser/dialogs/SaveDialog.kt delete mode 100644 app/src/main/java/com/stoutner/privacybrowser/fragments/AboutWebViewFragment.java create mode 100644 app/src/main/java/com/stoutner/privacybrowser/fragments/AboutWebViewFragment.kt rename app/src/main/res/layout/{pinned_mismatch_scrollview.xml => pinned_mismatch_tab_linearlayout.xml} (98%) diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/AboutActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/AboutActivity.java index 0ff3f825..7d0228fb 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/AboutActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/AboutActivity.java @@ -1,5 +1,5 @@ /* - * Copyright © 2016-2020 Soren Stoutner . + * Copyright © 2016-2021 Soren Stoutner . * * This file is part of Privacy Browser . * @@ -101,6 +101,9 @@ public class AboutActivity extends AppCompatActivity implements SaveDialog.SaveL // Store the blocklist versions. String[] blocklistVersions = launchingIntent.getStringArrayExtra("blocklist_versions"); + // Remove the incorrect lint warning below that the blocklist versions might be null. + assert blocklistVersions != null; + // Set the content view. setContentView(R.layout.about_coordinatorlayout); 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 689773cf..cfceab0d 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksActivity.java @@ -1,5 +1,5 @@ /* - * Copyright © 2016-2020 Soren Stoutner . + * Copyright © 2016-2021 Soren Stoutner . * * This file is part of Privacy Browser . * @@ -71,15 +71,9 @@ import java.util.ArrayList; public class BookmarksActivity extends AppCompatActivity implements CreateBookmarkDialog.CreateBookmarkListener, CreateBookmarkFolderDialog.CreateBookmarkFolderListener, EditBookmarkDialog.EditBookmarkListener, EditBookmarkFolderDialog.EditBookmarkFolderListener, MoveToFolderDialog.MoveToFolderListener { - // `currentFolder` is public static so it can be accessed from `MoveToFolderDialog` and `BookmarksDatabaseViewActivity`. - // It is used in `onCreate`, `onOptionsItemSelected()`, `onBackPressed()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveBookmark()`, `onSaveBookmarkFolder()`, `onMoveToFolder()`, - // and `loadFolder()`. + // `currentFolder` is public static so it can be accessed from `BookmarksDatabaseViewActivity`. 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()`. - public static long[] checkedItemIds; - // `restartFromBookmarksDatabaseViewActivity` is public static so it can be accessed from `BookmarksDatabaseViewActivity`. It is also used in `onRestart()`. public static boolean restartFromBookmarksDatabaseViewActivity; @@ -420,11 +414,8 @@ public class BookmarksActivity extends AppCompatActivity implements CreateBookma // Update the enabled status of the move icons. updateMoveIcons(); } else if (menuItemId == R.id.move_to_folder) { // Move to folder. - // Store the checked item IDs for use by the alert dialog. - checkedItemIds = bookmarksListView.getCheckedItemIds(); - // Instantiate the move to folder alert dialog. - DialogFragment moveToFolderDialog = new MoveToFolderDialog(); + DialogFragment moveToFolderDialog = MoveToFolderDialog.moveBookmarks(currentFolder, bookmarksListView.getCheckedItemIds()); // Show the move to folder alert dialog. moveToFolderDialog.show(getSupportFragmentManager(), getResources().getString(R.string.move_to_folder)); @@ -870,7 +861,7 @@ public class BookmarksActivity extends AppCompatActivity implements CreateBookma } @Override - public void onSaveBookmarkFolder(DialogFragment dialogFragment, int selectedFolderDatabaseId, Bitmap favoriteIconBitmap) { + public void onSaveBookmarkFolder(DialogFragment dialogFragment, int selectedFolderDatabaseId, @NonNull Bitmap favoriteIconBitmap) { // Get the dialog from the dialog fragment. Dialog dialog = dialogFragment.getDialog(); 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 5b39fdcb..e5d1f8c8 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java @@ -1,5 +1,5 @@ /* - * Copyright © 2015-2020 Soren Stoutner . + * Copyright © 2015-2021 Soren Stoutner . * * Download cookie code contributed 2017 Hendrik Knackstedt. Copyright assigned to Soren Stoutner . * @@ -649,11 +649,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook updatePrivacyIcons(true); } - // `onResume()` runs after `onStart()`, which runs after `onCreate()` and `onRestart()`. + // `onStart()` runs after `onCreate()` or `onRestart()`. This is used instead of `onResume()` so the commands aren't called every time the screen is partially hidden. @Override - public void onResume() { + public void onStart() { // Run the default commands. - super.onResume(); + super.onStart(); // Resume any WebViews. for (int i = 0; i < webViewPagerAdapter.getCount(); i++) { @@ -702,10 +702,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } } + // `onStop()` runs after `onPause()`. It is used instead of `onPause()` so the commands are not called every time the screen is partially hidden. @Override - public void onPause() { + public void onStop() { // Run the default commands. - super.onPause(); + super.onStop(); for (int i = 0; i < webViewPagerAdapter.getCount(); i++) { // Get the WebView tab fragment. @@ -2594,7 +2595,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } @Override - public void onSaveBookmarkFolder(DialogFragment dialogFragment, int selectedFolderDatabaseId, Bitmap favoriteIconBitmap) { + public void onSaveBookmarkFolder(DialogFragment dialogFragment, int selectedFolderDatabaseId, @NonNull Bitmap favoriteIconBitmap) { // Remove the incorrect lint warning below that the dialog fragment might be null. assert dialogFragment != null; diff --git a/app/src/main/java/com/stoutner/privacybrowser/adapters/AboutPagerAdapter.java b/app/src/main/java/com/stoutner/privacybrowser/adapters/AboutPagerAdapter.java deleted file mode 100644 index 27f97290..00000000 --- a/app/src/main/java/com/stoutner/privacybrowser/adapters/AboutPagerAdapter.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright © 2016-2020 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.adapters; - -import android.content.Context; - -import androidx.annotation.NonNull; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentPagerAdapter; - -import com.stoutner.privacybrowser.R; -import com.stoutner.privacybrowser.fragments.AboutVersionFragment; -import com.stoutner.privacybrowser.fragments.AboutWebViewFragment; - -import java.util.LinkedList; - -public class AboutPagerAdapter extends FragmentPagerAdapter { - // Define the class variables. - private final Context context; - private final String[] blocklistVersions; - private final LinkedList aboutFragmentList = new LinkedList<>(); - - public AboutPagerAdapter(FragmentManager fragmentManager, Context context, String[] blocklistVersions) { - // Run the default commands. - super(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT); - - // Store the class variables. - this.context = context; - this.blocklistVersions = blocklistVersions; - } - - @Override - // Get the count of the number of tabs. - public int getCount() { - return 7; - } - - @Override - // Get the name of each tab. Tab numbers start at 0. - public CharSequence getPageTitle(int tab) { - switch (tab) { - case 0: - return context.getString(R.string.version); - - case 1: - return context.getString(R.string.permissions); - - case 2: - return context.getString(R.string.privacy_policy); - - case 3: - return context.getString(R.string.changelog); - - case 4: - return context.getString(R.string.licenses); - - case 5: - return context.getString(R.string.contributors); - - case 6: - return context.getString(R.string.links); - - default: - return ""; - } - } - - @Override - @NonNull - // Setup each tab. - public Fragment getItem(int tabNumber) { - // Create the tab fragment and add it to the list. - if (tabNumber == 0){ - // Add the version tab to the list. - aboutFragmentList.add(AboutVersionFragment.createTab(blocklistVersions)); - } else { - // Add the WebView tab to the list. - aboutFragmentList.add(AboutWebViewFragment.createTab(tabNumber)); - } - - // Return the tab number fragment. - return aboutFragmentList.get(tabNumber); - } - - public Fragment getTabFragment(int tabNumber) { - // Return the tab fragment. - return aboutFragmentList.get(tabNumber); - } -} \ No newline at end of file diff --git a/app/src/main/java/com/stoutner/privacybrowser/adapters/AboutPagerAdapter.kt b/app/src/main/java/com/stoutner/privacybrowser/adapters/AboutPagerAdapter.kt new file mode 100644 index 00000000..51967501 --- /dev/null +++ b/app/src/main/java/com/stoutner/privacybrowser/adapters/AboutPagerAdapter.kt @@ -0,0 +1,79 @@ +/* + * Copyright © 2016-2021 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.adapters + +import android.content.Context + +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager +import androidx.fragment.app.FragmentPagerAdapter + +import com.stoutner.privacybrowser.R +import com.stoutner.privacybrowser.fragments.AboutVersionFragment +import com.stoutner.privacybrowser.fragments.AboutWebViewFragment + +import java.util.LinkedList + +class AboutPagerAdapter(fragmentManager: FragmentManager, private val context: Context, private val blocklistVersions: Array) : + FragmentPagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { + // Define the class variables. + private val aboutFragmentList = LinkedList() + + // Get the number of tabs. + override fun getCount(): Int { + // There are seven tabs. + return 7 + } + + // Get the name of each tab. Tab numbers start at 0. + override fun getPageTitle(tab: Int): CharSequence { + return when (tab) { + 0 -> context.getString(R.string.version) + 1 -> context.getString(R.string.permissions) + 2 -> context.getString(R.string.privacy_policy) + 3 -> context.getString(R.string.changelog) + 4 -> context.getString(R.string.licenses) + 5 -> context.getString(R.string.contributors) + 6 -> context.getString(R.string.links) + else -> "" + } + } + + // Setup each tab. + override fun getItem(tabNumber: Int): Fragment { + // Create the tab fragment and add it to the list. + if (tabNumber == 0) { + // Add the version tab to the list. + aboutFragmentList.add(AboutVersionFragment.createTab(blocklistVersions)) + } else { + // Add the WebView tab to the list. + aboutFragmentList.add(AboutWebViewFragment.createTab(tabNumber)) + } + + // Return the tab fragment. + return aboutFragmentList[tabNumber] + } + + // Get a tab. + fun getTabFragment(tabNumber: Int): Fragment { + // Return the tab fragment. + return aboutFragmentList[tabNumber] + } +} \ No newline at end of file diff --git a/app/src/main/java/com/stoutner/privacybrowser/adapters/PinnedMismatchPagerAdapter.kt b/app/src/main/java/com/stoutner/privacybrowser/adapters/PinnedMismatchPagerAdapter.kt new file mode 100644 index 00000000..6156533a --- /dev/null +++ b/app/src/main/java/com/stoutner/privacybrowser/adapters/PinnedMismatchPagerAdapter.kt @@ -0,0 +1,302 @@ +/* + * Copyright © 2021 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.adapters + +import android.content.Context +import android.content.res.Configuration +import android.net.Uri +import android.text.SpannableStringBuilder +import android.text.Spanned +import android.text.style.ForegroundColorSpan +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView + +import androidx.viewpager.widget.PagerAdapter + +import com.stoutner.privacybrowser.R +import com.stoutner.privacybrowser.activities.MainWebViewActivity +import com.stoutner.privacybrowser.views.NestedScrollWebView + +import java.text.DateFormat +import java.util.Date + +// This adapter uses a PagerAdapter instead of a FragmentPagerAdapter because dialogs fragments really don't like having a nested FragmentPagerAdapter inside of them. +class PinnedMismatchPagerAdapter(private val context: Context, private val layoutInflater: LayoutInflater, private val webViewFragmentId: Long) : PagerAdapter() { + override fun isViewFromObject(view: View, `object`: Any): Boolean { + // Check to see if the view and the object are the same. + return view === `object` + } + + // Get the number of tabs. + override fun getCount(): Int { + // There are two tabs. + return 2 + } + + // Get the name of each tab. Tab numbers start at 0. + override fun getPageTitle(tabNumber: Int): CharSequence { + return when (tabNumber) { + 0 -> context.getString(R.string.current) + 1 -> context.getString(R.string.pinned) + else -> "" + } + } + + // Setup each tab. + override fun instantiateItem(container: ViewGroup, tabNumber: Int): Any { + // Get the current position of this WebView fragment. + val webViewPosition = MainWebViewActivity.webViewPagerAdapter.getPositionForId(webViewFragmentId) + + // Get the WebView tab fragment. + val webViewTabFragment = MainWebViewActivity.webViewPagerAdapter.getPageFragment(webViewPosition) + + // Get the WebView fragment view. + val webViewFragmentView = webViewTabFragment.requireView() + + // Get a handle for the current WebView. + val nestedScrollWebView = webViewFragmentView.findViewById(R.id.nestedscroll_webview)!! + + // Inflate the scroll view for this tab. + val tabLayout = layoutInflater.inflate(R.layout.pinned_mismatch_tab_linearlayout, container, false) as ViewGroup + + // Get handles for the views. + val domainNameTextView = tabLayout.findViewById(R.id.domain_name) + val ipAddressesTextView = tabLayout.findViewById(R.id.ip_addresses) + val issuedToCNameTextView = tabLayout.findViewById(R.id.issued_to_cname) + val issuedToONameTextView = tabLayout.findViewById(R.id.issued_to_oname) + val issuedToUNameTextView = tabLayout.findViewById(R.id.issued_to_uname) + val issuedByCNameTextView = tabLayout.findViewById(R.id.issued_by_cname) + val issuedByONameTextView = tabLayout.findViewById(R.id.issued_by_oname) + val issuedByUNameTextView = tabLayout.findViewById(R.id.issued_by_uname) + val startDateTextView = tabLayout.findViewById(R.id.start_date) + val endDateTextView = tabLayout.findViewById(R.id.end_date) + + // Setup the labels. + val domainNameLabel = context.getString(R.string.domain_label) + " " + val ipAddressesLabel = context.getString(R.string.ip_addresses) + " " + val cNameLabel = context.getString(R.string.common_name) + " " + val oNameLabel = context.getString(R.string.organization) + " " + val uNameLabel = context.getString(R.string.organizational_unit) + " " + val startDateLabel = context.getString(R.string.start_date) + " " + val endDateLabel = context.getString(R.string.end_date) + " " + + // Convert the URL to a URI. + val currentUri = Uri.parse(nestedScrollWebView.url) + + // Get the current host from the URI. + val domainName = currentUri.host + + // Get the current website SSL certificate. + val sslCertificate = nestedScrollWebView.certificate + + // Initialize the SSL certificate variables. + var currentSslIssuedToCName = "" + var currentSslIssuedToOName = "" + var currentSslIssuedToUName = "" + var currentSslIssuedByCName = "" + var currentSslIssuedByOName = "" + var currentSslIssuedByUName = "" + var currentSslStartDate: Date? = null + var currentSslEndDate: Date? = null + + // Extract the individual pieces of information from the current website SSL certificate if it is not null. + if (sslCertificate != null) { + currentSslIssuedToCName = sslCertificate.issuedTo.cName + currentSslIssuedToOName = sslCertificate.issuedTo.oName + currentSslIssuedToUName = sslCertificate.issuedTo.uName + currentSslIssuedByCName = sslCertificate.issuedBy.cName + currentSslIssuedByOName = sslCertificate.issuedBy.oName + currentSslIssuedByUName = sslCertificate.issuedBy.uName + currentSslStartDate = sslCertificate.validNotBeforeDate + currentSslEndDate = sslCertificate.validNotAfterDate + } + + // Get the pinned SSL certificate. + val pinnedSslCertificateArrayList = nestedScrollWebView.pinnedSslCertificate + + // Extract the arrays from the array list. + val pinnedSslCertificateStringArray = pinnedSslCertificateArrayList[0] as Array<*> + val pinnedSslCertificateDateArray = pinnedSslCertificateArrayList[1] as Array<*> + + // Setup the domain name spannable string builder. + val domainNameStringBuilder = SpannableStringBuilder(domainNameLabel + domainName) + + // Initialize the spannable string builders. + val ipAddressesStringBuilder: SpannableStringBuilder + val issuedToCNameStringBuilder: SpannableStringBuilder + val issuedToONameStringBuilder: SpannableStringBuilder + val issuedToUNameStringBuilder: SpannableStringBuilder + val issuedByCNameStringBuilder: SpannableStringBuilder + val issuedByONameStringBuilder: SpannableStringBuilder + val issuedByUNameStringBuilder: SpannableStringBuilder + val startDateStringBuilder: SpannableStringBuilder + val endDateStringBuilder: SpannableStringBuilder + + // Setup the spannable string builders for each tab. + if (tabNumber == 0) { // Setup the current settings tab. + // Create the string builders. + ipAddressesStringBuilder = SpannableStringBuilder(ipAddressesLabel + nestedScrollWebView.currentIpAddresses) + issuedToCNameStringBuilder = SpannableStringBuilder(cNameLabel + currentSslIssuedToCName) + issuedToONameStringBuilder = SpannableStringBuilder(oNameLabel + currentSslIssuedToOName) + issuedToUNameStringBuilder = SpannableStringBuilder(uNameLabel + currentSslIssuedToUName) + issuedByCNameStringBuilder = SpannableStringBuilder(cNameLabel + currentSslIssuedByCName) + issuedByONameStringBuilder = SpannableStringBuilder(oNameLabel + currentSslIssuedByOName) + issuedByUNameStringBuilder = SpannableStringBuilder(uNameLabel + currentSslIssuedByUName) + + // Set the dates if they aren't null. Formatting a null date causes a crash. + startDateStringBuilder = if (currentSslStartDate == null) { + SpannableStringBuilder(startDateLabel) + } else { + SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(currentSslStartDate)) + } + + endDateStringBuilder = if (currentSslEndDate == null) { + SpannableStringBuilder(endDateLabel) + } else { + SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(currentSslEndDate)) + } + } else { // Setup the pinned settings tab. + // Create the string builders. + ipAddressesStringBuilder = SpannableStringBuilder(ipAddressesLabel + nestedScrollWebView.pinnedIpAddresses) + issuedToCNameStringBuilder = SpannableStringBuilder(cNameLabel + pinnedSslCertificateStringArray[0]) + issuedToONameStringBuilder = SpannableStringBuilder(oNameLabel + pinnedSslCertificateStringArray[1]) + issuedToUNameStringBuilder = SpannableStringBuilder(uNameLabel + pinnedSslCertificateStringArray[2]) + issuedByCNameStringBuilder = SpannableStringBuilder(cNameLabel + pinnedSslCertificateStringArray[3]) + issuedByONameStringBuilder = SpannableStringBuilder(oNameLabel + pinnedSslCertificateStringArray[4]) + issuedByUNameStringBuilder = SpannableStringBuilder(uNameLabel + pinnedSslCertificateStringArray[5]) + + // Set the dates if they aren't null. Formatting a null date causes a crash. + startDateStringBuilder = if (pinnedSslCertificateDateArray[0] == null) { + SpannableStringBuilder(startDateLabel) + } else { + SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(pinnedSslCertificateDateArray[0])) + } + + endDateStringBuilder = if (pinnedSslCertificateDateArray[1] == null) { + SpannableStringBuilder(endDateLabel) + } else { + SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(pinnedSslCertificateDateArray[1])) + } + } + + // Define the color spans. + val blueColorSpan: ForegroundColorSpan + val redColorSpan: ForegroundColorSpan + + // Get the current theme status. + val currentThemeStatus = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK + + // Set the color spans according to the theme. The deprecated `resources` must be used until the minimum API >= 23. + if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { + @Suppress("DEPRECATION") + blueColorSpan = ForegroundColorSpan(context.resources.getColor(R.color.blue_700)) + @Suppress("DEPRECATION") + redColorSpan = ForegroundColorSpan(context.resources.getColor(R.color.red_a700)) + } else { + @Suppress("DEPRECATION") + blueColorSpan = ForegroundColorSpan(context.resources.getColor(R.color.violet_700)) + @Suppress("DEPRECATION") + redColorSpan = ForegroundColorSpan(context.resources.getColor(R.color.red_900)) + } + + // Set the domain name to be blue. + domainNameStringBuilder.setSpan(blueColorSpan, domainNameLabel.length, domainNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + + // Color coordinate the IP addresses if they are pinned. + if (nestedScrollWebView.hasPinnedIpAddresses()) { + if (nestedScrollWebView.currentIpAddresses == nestedScrollWebView.pinnedIpAddresses) { + ipAddressesStringBuilder.setSpan(blueColorSpan, ipAddressesLabel.length, ipAddressesStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + } else { + ipAddressesStringBuilder.setSpan(redColorSpan, ipAddressesLabel.length, ipAddressesStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + } + } + + // Color coordinate the SSL certificate fields if they are pinned. + if (nestedScrollWebView.hasPinnedSslCertificate()) { + if (currentSslIssuedToCName == pinnedSslCertificateStringArray[0]) { + issuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, issuedToCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + } else { + issuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, issuedToCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + } + + if (currentSslIssuedToOName == pinnedSslCertificateStringArray[1]) { + issuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length, issuedToONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + } else { + issuedToONameStringBuilder.setSpan(redColorSpan, oNameLabel.length, issuedToONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + } + + if (currentSslIssuedToUName == pinnedSslCertificateStringArray[2]) { + issuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length, issuedToUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + } else { + issuedToUNameStringBuilder.setSpan(redColorSpan, uNameLabel.length, issuedToUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + } + + if (currentSslIssuedByCName == pinnedSslCertificateStringArray[3]) { + issuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, issuedByCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + } else { + issuedByCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, issuedByCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + } + + if (currentSslIssuedByOName == pinnedSslCertificateStringArray[4]) { + issuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length, issuedByONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + } else { + issuedByONameStringBuilder.setSpan(redColorSpan, oNameLabel.length, issuedByONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + } + + if (currentSslIssuedByUName == pinnedSslCertificateStringArray[5]) { + issuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length, issuedByUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + } else { + issuedByUNameStringBuilder.setSpan(redColorSpan, uNameLabel.length, issuedByUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + } + + if (currentSslStartDate != null && currentSslStartDate == pinnedSslCertificateDateArray[0]) { + startDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length, startDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + } else { + startDateStringBuilder.setSpan(redColorSpan, startDateLabel.length, startDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + } + + if (currentSslEndDate != null && currentSslEndDate == pinnedSslCertificateDateArray[1]) { + endDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length, endDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + } else { + endDateStringBuilder.setSpan(redColorSpan, endDateLabel.length, endDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + } + } + + // Display the strings. + domainNameTextView.text = domainNameStringBuilder + ipAddressesTextView.text = ipAddressesStringBuilder + issuedToCNameTextView.text = issuedToCNameStringBuilder + issuedToONameTextView.text = issuedToONameStringBuilder + issuedToUNameTextView.text = issuedToUNameStringBuilder + issuedByCNameTextView.text = issuedByCNameStringBuilder + issuedByONameTextView.text = issuedByONameStringBuilder + issuedByUNameTextView.text = issuedByUNameStringBuilder + startDateTextView.text = startDateStringBuilder + endDateTextView.text = endDateStringBuilder + + // Add the tab layout to the container. This needs to be manually done for pager adapters. + container.addView(tabLayout) + + // Return the tab layout. + return tabLayout + } +} \ No newline at end of file diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/AboutViewSourceDialog.kt b/app/src/main/java/com/stoutner/privacybrowser/dialogs/AboutViewSourceDialog.kt index 1b9161eb..5089fa22 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/AboutViewSourceDialog.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/AboutViewSourceDialog.kt @@ -1,5 +1,5 @@ /* - * Copyright © 2018-2020 Soren Stoutner . + * Copyright © 2018-2021 Soren Stoutner . * * This file is part of Privacy Browser . * @@ -29,7 +29,7 @@ import androidx.preference.PreferenceManager import com.stoutner.privacybrowser.R -class AboutViewSourceDialog: DialogFragment() { +class AboutViewSourceDialog : DialogFragment() { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { // Use a builder to create the alert dialog. val dialogBuilder: AlertDialog.Builder = AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog) diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/AddDomainDialog.kt b/app/src/main/java/com/stoutner/privacybrowser/dialogs/AddDomainDialog.kt index 9860a04a..464a24ab 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/AddDomainDialog.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/AddDomainDialog.kt @@ -1,5 +1,5 @@ /* - * Copyright © 2017-2020 Soren Stoutner . + * Copyright © 2017-2021 Soren Stoutner . * * This file is part of Privacy Browser . * @@ -43,15 +43,15 @@ import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper // Declare the class constants. private const val URL_STRING = "url_string" -class AddDomainDialog: DialogFragment() { +class AddDomainDialog : DialogFragment() { + // Declare the class variables + private lateinit var addDomainListener: AddDomainListener + // The public interface is used to send information back to the parent activity. interface AddDomainListener { fun onAddDomain(dialogFragment: DialogFragment) } - // Declare the class variables - private lateinit var addDomainListener: AddDomainListener - override fun onAttach(context: Context) { // Run the default commands. super.onAttach(context) @@ -61,7 +61,7 @@ class AddDomainDialog: DialogFragment() { } companion object { - // `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin. Also, the function can then be moved out of a companion object and just become a package-level function. + // `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin. @JvmStatic fun addDomain(urlString: String): AddDomainDialog { // Create an arguments bundle. @@ -102,10 +102,10 @@ class AddDomainDialog: DialogFragment() { // Set the view. The parent view is `null` because it will be assigned by the alert dialog. dialogBuilder.setView(requireActivity().layoutInflater.inflate(R.layout.add_domain_dialog, null)) - // Set a listener on the cancel button. Using `null` as the listener closes the dialog without doing anything else. + // Set the cancel button listener. Using `null` as the listener closes the dialog without doing anything else. dialogBuilder.setNegativeButton(R.string.cancel, null) - // Set a listener on the add button. + // Set the add button listener. dialogBuilder.setPositiveButton(R.string.add) { _: DialogInterface, _: Int -> // Return the dialog fragment to the parent activity on add. addDomainListener.onAddDomain(this) diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateBookmarkDialog.kt b/app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateBookmarkDialog.kt index c3871863..02832137 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateBookmarkDialog.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateBookmarkDialog.kt @@ -1,5 +1,5 @@ /* - * Copyright © 2016-2020 Soren Stoutner . + * Copyright © 2016-2021 Soren Stoutner . * * This file is part of Privacy Browser . * @@ -46,15 +46,15 @@ private const val URL_STRING = "url_string" private const val TITLE = "title" private const val FAVORITE_ICON_BYTE_ARRAY = "favorite_icon_byte_array" -class CreateBookmarkDialog: DialogFragment() { +class CreateBookmarkDialog : DialogFragment() { + // Declare the class variables + private lateinit var createBookmarkListener: CreateBookmarkListener + // The public interface is used to send information back to the parent activity. interface CreateBookmarkListener { fun onCreateBookmark(dialogFragment: DialogFragment, favoriteIconBitmap: Bitmap) } - // Declare the class variables - private lateinit var createBookmarkListener: CreateBookmarkListener - override fun onAttach(context: Context) { // Run the default commands. super.onAttach(context) @@ -64,7 +64,7 @@ class CreateBookmarkDialog: DialogFragment() { } companion object { - // `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin. Also, the function can then be moved out of a companion object and just become a package-level function. + // `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin. @JvmStatic fun createBookmark(urlString: String, title: String, favoriteIconBitmap: Bitmap): CreateBookmarkDialog { // Create a favorite icon byte array output stream. diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateBookmarkFolderDialog.kt b/app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateBookmarkFolderDialog.kt index 70546649..d5fd6d82 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateBookmarkFolderDialog.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateBookmarkFolderDialog.kt @@ -1,5 +1,5 @@ /* - * Copyright © 2016-2020 Soren Stoutner . + * Copyright © 2016-2021 Soren Stoutner . * * This file is part of Privacy Browser . * @@ -46,15 +46,15 @@ import java.io.ByteArrayOutputStream // Declare the class constants. private const val FAVORITE_ICON_BYTE_ARRAY = "favorite_icon_byte_array" -class CreateBookmarkFolderDialog: DialogFragment() { +class CreateBookmarkFolderDialog : DialogFragment() { + // Declare the class variables. + private lateinit var createBookmarkFolderListener: CreateBookmarkFolderListener + // The public interface is used to send information back to the parent activity. interface CreateBookmarkFolderListener { fun onCreateBookmarkFolder(dialogFragment: DialogFragment, favoriteIconBitmap: Bitmap) } - // Declare the class variables. - private lateinit var createBookmarkFolderListener: CreateBookmarkFolderListener - override fun onAttach(context: Context) { // Run the default commands. super.onAttach(context) @@ -64,7 +64,7 @@ class CreateBookmarkFolderDialog: DialogFragment() { } companion object { - // `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin. Also, the function can then be moved out of a companion object and just become a package-level function. + // `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin. @JvmStatic fun createBookmarkFolder(favoriteIconBitmap: Bitmap): CreateBookmarkFolderDialog { // Create a favorite icon byte array output stream. diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateHomeScreenShortcutDialog.kt b/app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateHomeScreenShortcutDialog.kt index 21e40d47..b0df47fa 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateHomeScreenShortcutDialog.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/CreateHomeScreenShortcutDialog.kt @@ -1,5 +1,5 @@ /* - * Copyright © 2015-2020 Soren Stoutner . + * Copyright © 2015-2021 Soren Stoutner . * * This file is part of Privacy Browser . * @@ -55,14 +55,14 @@ private const val SHORTCUT_NAME = "shortcut_name" private const val URL_STRING = "url_string" private const val FAVORITE_ICON_BYTE_ARRAY = "favorite_icon_byte_array" -class CreateHomeScreenShortcutDialog: DialogFragment() { +class CreateHomeScreenShortcutDialog : DialogFragment() { // Declare the class views. private lateinit var shortcutNameEditText: EditText private lateinit var urlEditText: EditText private lateinit var openWithPrivacyBrowserRadioButton: RadioButton companion object { - // `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin. Also, the function can then be moved out of a companion object and just become a package-level function. + // `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin. @JvmStatic fun createDialog(shortcutName: String, urlString: String, favoriteIconBitmap: Bitmap): CreateHomeScreenShortcutDialog { // Create a favorite icon byte array output stream. diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDatabaseViewDialog.kt b/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDatabaseViewDialog.kt index eb445a16..c0ace69f 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDatabaseViewDialog.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDatabaseViewDialog.kt @@ -1,5 +1,5 @@ /* - * Copyright © 2016-2020 Soren Stoutner . + * Copyright © 2016-2021 Soren Stoutner . * * This file is part of Privacy Browser . * @@ -60,12 +60,7 @@ import java.io.ByteArrayOutputStream private const val DATABASE_ID = "database_id" private const val FAVORITE_ICON_BYTE_ARRAY = "favorite_icon_byte_array" -class EditBookmarkDatabaseViewDialog: DialogFragment() { - // The public interface is used to send information back to the parent activity. - interface EditBookmarkDatabaseViewListener { - fun onSaveBookmark(dialogFragment: DialogFragment, selectedBookmarkDatabaseId: Int, favoriteIconBitmap: Bitmap) - } - +class EditBookmarkDatabaseViewDialog : DialogFragment() { // Declare the class variables. private lateinit var editBookmarkDatabaseViewListener: EditBookmarkDatabaseViewListener @@ -77,6 +72,11 @@ class EditBookmarkDatabaseViewDialog: DialogFragment() { private lateinit var displayOrderEditText: EditText private lateinit var saveButton: Button + // The public interface is used to send information back to the parent activity. + interface EditBookmarkDatabaseViewListener { + fun onSaveBookmark(dialogFragment: DialogFragment, selectedBookmarkDatabaseId: Int, favoriteIconBitmap: Bitmap) + } + override fun onAttach(context: Context) { // Run the default commands. super.onAttach(context) @@ -86,7 +86,7 @@ class EditBookmarkDatabaseViewDialog: DialogFragment() { } companion object { - // `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin. Also, the function can then be moved out of a companion object and just become a package-level function. + // `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin. @JvmStatic fun bookmarkDatabaseId(databaseId: Int, favoriteIconBitmap: Bitmap): EditBookmarkDatabaseViewDialog { // Create a favorite icon byte array output stream. diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDialog.kt b/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDialog.kt index 356de47b..84308bf0 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDialog.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDialog.kt @@ -1,5 +1,5 @@ /* - * Copyright © 2016-2020 Soren Stoutner . + * Copyright © 2016-2021 Soren Stoutner . * * This file is part of Privacy Browser . * @@ -50,12 +50,7 @@ import java.io.ByteArrayOutputStream private const val DATABASE_ID = "database_id" private const val FAVORITE_ICON_BYTE_ARRAY = "favorite_icon_byte_array" -class EditBookmarkDialog: DialogFragment() { - // The public interface is used to send information back to the parent activity. - interface EditBookmarkListener { - fun onSaveBookmark(dialogFragment: DialogFragment, selectedBookmarkDatabaseId: Int, favoriteIconBitmap: Bitmap) - } - +class EditBookmarkDialog : DialogFragment() { // Declare the class variables. private lateinit var editBookmarkListener: EditBookmarkListener @@ -65,6 +60,11 @@ class EditBookmarkDialog: DialogFragment() { private lateinit var newIconRadioButton: RadioButton private lateinit var saveButton: Button + // The public interface is used to send information back to the parent activity. + interface EditBookmarkListener { + fun onSaveBookmark(dialogFragment: DialogFragment, selectedBookmarkDatabaseId: Int, favoriteIconBitmap: Bitmap) + } + override fun onAttach(context: Context) { // Run the default commands. super.onAttach(context) @@ -74,7 +74,7 @@ class EditBookmarkDialog: DialogFragment() { } companion object { - // `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin. Also, the function can then be moved out of a companion object and just become a package-level function. + // `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin. @JvmStatic fun bookmarkDatabaseId(databaseId: Int, favoriteIconBitmap: Bitmap): EditBookmarkDialog { // Create a favorite icon byte array output stream. diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDatabaseViewDialog.kt b/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDatabaseViewDialog.kt index 9c5a3273..e319de03 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDatabaseViewDialog.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDatabaseViewDialog.kt @@ -1,5 +1,5 @@ /* - * Copyright © 2016-2020 Soren Stoutner . + * Copyright © 2016-2021 Soren Stoutner . * * This file is part of Privacy Browser . * @@ -54,12 +54,7 @@ import java.io.ByteArrayOutputStream private const val DATABASE_ID = "database_id" private const val FAVORITE_ICON_BYTE_ARRAY = "favorite_icon_byte_array" -class EditBookmarkFolderDatabaseViewDialog: DialogFragment() { - // The public interface is used to send information back to the parent activity. - interface EditBookmarkFolderDatabaseViewListener { - fun onSaveBookmarkFolder(dialogFragment: DialogFragment, selectedFolderDatabaseId: Int, favoriteIconBitmap: Bitmap) - } - +class EditBookmarkFolderDatabaseViewDialog : DialogFragment() { // Declare the class variables. private lateinit var editBookmarkFolderDatabaseViewListener: EditBookmarkFolderDatabaseViewListener @@ -70,6 +65,11 @@ class EditBookmarkFolderDatabaseViewDialog: DialogFragment() { private lateinit var currentIconRadioButton: RadioButton private lateinit var saveButton: Button + // The public interface is used to send information back to the parent activity. + interface EditBookmarkFolderDatabaseViewListener { + fun onSaveBookmarkFolder(dialogFragment: DialogFragment, selectedFolderDatabaseId: Int, favoriteIconBitmap: Bitmap) + } + override fun onAttach(context: Context) { // Run the default commands. super.onAttach(context) @@ -79,7 +79,7 @@ class EditBookmarkFolderDatabaseViewDialog: DialogFragment() { } companion object { - // `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin. Also, the function can then be moved out of a companion object and just become a package-level function. + // `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin. @JvmStatic fun folderDatabaseId(databaseId: Int, favoriteIconBitmap: Bitmap): EditBookmarkFolderDatabaseViewDialog { // Create a favorite icon byte array output stream. diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDialog.kt b/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDialog.kt index ea4ea4d4..578fcd28 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDialog.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDialog.kt @@ -1,5 +1,5 @@ /* - * Copyright © 2016-2020 Soren Stoutner . + * Copyright © 2016-2021 Soren Stoutner . * * This file is part of Privacy Browser . * @@ -50,12 +50,7 @@ import java.io.ByteArrayOutputStream private const val DATABASE_ID = "database_id" private const val FAVORITE_ICON_BYTE_ARRAY = "favorite_icon_byte_array" -class EditBookmarkFolderDialog: DialogFragment() { - // The public interface is used to send information back to the parent activity. - interface EditBookmarkFolderListener { - fun onSaveBookmarkFolder(dialogFragment: DialogFragment?, selectedFolderDatabaseId: Int, favoriteIconBitmap: Bitmap?) - } - +class EditBookmarkFolderDialog : DialogFragment() { // Declare the class variables. private lateinit var editBookmarkFolderListener: EditBookmarkFolderListener private lateinit var bookmarksDatabaseHelper: BookmarksDatabaseHelper @@ -66,6 +61,11 @@ class EditBookmarkFolderDialog: DialogFragment() { private lateinit var folderNameEditText: EditText private lateinit var saveButton: Button + // The public interface is used to send information back to the parent activity. + interface EditBookmarkFolderListener { + fun onSaveBookmarkFolder(dialogFragment: DialogFragment, selectedFolderDatabaseId: Int, favoriteIconBitmap: Bitmap) + } + override fun onAttach(context: Context) { // Run the default commands. super.onAttach(context) @@ -75,7 +75,7 @@ class EditBookmarkFolderDialog: DialogFragment() { } companion object { - // `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin. Also, the function can then be moved out of a companion object and just become a package-level function. + // `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin. @JvmStatic fun folderDatabaseId(databaseId: Int, favoriteIconBitmap: Bitmap): EditBookmarkFolderDialog { // Create a favorite icon byte array output stream. diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/FontSizeDialog.kt b/app/src/main/java/com/stoutner/privacybrowser/dialogs/FontSizeDialog.kt index c8f3de89..80593fd9 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/FontSizeDialog.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/FontSizeDialog.kt @@ -1,5 +1,5 @@ /* - * Copyright © 2019-2020 Soren Stoutner . + * Copyright © 2019-2021 Soren Stoutner . * * This file is part of Privacy Browser . * @@ -39,15 +39,15 @@ import com.stoutner.privacybrowser.R // Declare the class constants. private const val FONT_SIZE = "font_size" -class FontSizeDialog: DialogFragment() { +class FontSizeDialog : DialogFragment() { + // Declare the class variables. + private lateinit var updateFontSizeListener: UpdateFontSizeListener + // The public interface is used to send information back to the parent activity. interface UpdateFontSizeListener { fun onApplyNewFontSize(dialogFragment: DialogFragment?) } - // Declare the class variables. - private lateinit var updateFontSizeListener: UpdateFontSizeListener - override fun onAttach(context: Context) { // Run the default commands. super.onAttach(context) @@ -57,7 +57,7 @@ class FontSizeDialog: DialogFragment() { } companion object { - // `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin. Also, the function can then be moved out of a companion object and just become a package-level function. + // `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin. @JvmStatic fun displayDialog(fontSize: Int): FontSizeDialog { // Create an arguments bundle. diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/HttpAuthenticationDialog.kt b/app/src/main/java/com/stoutner/privacybrowser/dialogs/HttpAuthenticationDialog.kt index 8ed2a73f..b3cf7074 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/HttpAuthenticationDialog.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/HttpAuthenticationDialog.kt @@ -1,5 +1,5 @@ /* - * Copyright © 2017-2020 Soren Stoutner . + * Copyright © 2017-2021 Soren Stoutner . * * This file is part of Privacy Browser . * @@ -47,7 +47,7 @@ private const val HOST = "host" private const val REALM = "realm" private const val WEBVIEW_FRAGMENT_ID = "webview_fragment_id" -class HttpAuthenticationDialog: DialogFragment() { +class HttpAuthenticationDialog : DialogFragment() { // Define the class variables. private var dismissDialog: Boolean = false @@ -56,7 +56,7 @@ class HttpAuthenticationDialog: DialogFragment() { private lateinit var passwordEditText: EditText companion object { - // `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin. Also, the function can then be moved out of a companion object and just become a package-level function. + // `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin. @JvmStatic fun displayDialog(host: String, realm: String, webViewFragmentId: Long): HttpAuthenticationDialog { // Create an arguments bundle. diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/MoveToFolderDialog.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/MoveToFolderDialog.java deleted file mode 100644 index a421177c..00000000 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/MoveToFolderDialog.java +++ /dev/null @@ -1,322 +0,0 @@ -/* - * Copyright © 2016-2020 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.annotation.SuppressLint; -import android.app.Dialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.SharedPreferences; -import android.content.res.Configuration; -import android.database.Cursor; -import android.database.DatabaseUtils; -import android.database.MatrixCursor; -import android.database.MergeCursor; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.widget.AdapterView; -import android.widget.Button; -import android.widget.CursorAdapter; -import android.widget.ImageView; -import android.widget.ListView; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.appcompat.app.AlertDialog; -import androidx.core.content.ContextCompat; -import androidx.fragment.app.DialogFragment; - -import com.stoutner.privacybrowser.R; -import com.stoutner.privacybrowser.activities.BookmarksActivity; -import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper; - -import java.io.ByteArrayOutputStream; - -public class MoveToFolderDialog extends DialogFragment { - // Instantiate the class variables. - private MoveToFolderListener moveToFolderListener; - private BookmarksDatabaseHelper bookmarksDatabaseHelper; - private StringBuilder exceptFolders; - - // The public interface is used to send information back to the parent activity. - public interface MoveToFolderListener { - void onMoveToFolder(DialogFragment dialogFragment); - } - - public void onAttach(@NonNull Context context) { - // Run the default commands. - super.onAttach(context); - - // Get a handle for `MoveToFolderListener` from the launching context. - moveToFolderListener = (MoveToFolderListener) context; - } - - // `@SuppressLint("InflateParams")` removes the warning about using `null` as the parent view group when inflating the alert dialog. - @SuppressLint("InflateParams") - @Override - @NonNull - public Dialog onCreateDialog(Bundle savedInstanceState) { - // Initialize the database helper. The `0` specifies a database version, but that is ignored and set instead using a constant in `BookmarksDatabaseHelper`. - bookmarksDatabaseHelper = new BookmarksDatabaseHelper(getContext(), null, null, 0); - - // Use an alert dialog builder to create the alert dialog. - AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog); - - // Get the current theme status. - int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; - - // Set the icon according to the theme. - if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { - dialogBuilder.setIcon(R.drawable.move_to_folder_blue_day); - } else { - dialogBuilder.setIcon(R.drawable.move_to_folder_blue_night); - } - - // Set the title. - dialogBuilder.setTitle(R.string.move_to_folder); - - // Remove the incorrect lint warning that `getActivity()` might be null. - assert getActivity() != null; - - // Set the view. The parent view is `null` because it will be assigned by `AlertDialog`. - dialogBuilder.setView(getActivity().getLayoutInflater().inflate(R.layout.move_to_folder_dialog, null)); - - // Set the listener for the negative button. - dialogBuilder.setNegativeButton(R.string.cancel, (DialogInterface dialog, int which) -> { - // Do nothing. The `AlertDialog` will close automatically. - }); - - // Set the listener fo the positive button. - dialogBuilder.setPositiveButton(R.string.move, (DialogInterface dialog, int which) -> { - // Return the `DialogFragment` to the parent activity on save. - moveToFolderListener.onMoveToFolder(MoveToFolderDialog.this); - }); - - // Create an alert dialog from the alert dialog builder. - final AlertDialog alertDialog = dialogBuilder.create(); - - // Get a handle for the shared preferences. - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext()); - - // Get the screenshot preference. - boolean allowScreenshots = sharedPreferences.getBoolean("allow_screenshots", false); - - // Disable screenshots if not allowed. - if (!allowScreenshots) { - // Remove the warning below that `getWindow()` might be null. - assert alertDialog.getWindow() != null; - - // Disable screenshots. - alertDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE); - } - - // Show the alert dialog so the items in the layout can be modified. - alertDialog.show(); - - // Get a handle for the positive button. - final Button moveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE); - - // Initially disable the positive button. - moveButton.setEnabled(false); - - // Initialize the variables. - Cursor foldersCursor; - CursorAdapter foldersCursorAdapter; - exceptFolders = new StringBuilder(); - - // Check to see if we are in the `Home Folder`. - if (BookmarksActivity.currentFolder.isEmpty()) { // Don't display `Home Folder` at the top of the `ListView`. - // If a folder is selected, add it and all children to the list of folders not to display. - long[] selectedBookmarksLongArray = BookmarksActivity.checkedItemIds; - for (long databaseIdLong : selectedBookmarksLongArray) { - // Get `databaseIdInt` for each selected bookmark. - int databaseIdInt = (int) databaseIdLong; - - // If `databaseIdInt` is a folder. - if (bookmarksDatabaseHelper.isFolder(databaseIdInt)) { - // Get the name of the selected folder. - String folderName = bookmarksDatabaseHelper.getFolderName(databaseIdInt); - - // Populate the list of folders not to get. - if (exceptFolders.toString().isEmpty()){ - // Add the selected folder to the list of folders not to display. - exceptFolders.append(DatabaseUtils.sqlEscapeString(folderName)); - } else { - // Add the selected folder to the end of the list of folders not to display. - exceptFolders.append(","); - exceptFolders.append(DatabaseUtils.sqlEscapeString(folderName)); - } - - // Add the selected folder's subfolders to the list of folders not to display. - addSubfoldersToExceptFolders(folderName); - } - } - - // Get a cursor containing the folders to display. - foldersCursor = bookmarksDatabaseHelper.getFoldersExcept(exceptFolders.toString()); - - // Setup `foldersCursorAdaptor` with `this` context. `false` disables autoRequery. - foldersCursorAdapter = new CursorAdapter(alertDialog.getContext(), foldersCursor, false) { - @Override - public View newView(Context context, Cursor cursor, ViewGroup parent) { - // Remove the incorrect lint warning that `.getLayoutInflater()` might be false. - assert getActivity() != null; - - // Inflate the individual item layout. `false` does not attach it to the root. - return getActivity().getLayoutInflater().inflate(R.layout.move_to_folder_item_linearlayout, parent, false); - } - - @Override - public void bindView(View view, Context context, Cursor cursor) { - // Get the folder icon from `cursor`. - byte[] folderIconByteArray = cursor.getBlob(cursor.getColumnIndex(BookmarksDatabaseHelper.FAVORITE_ICON)); - // Convert the byte array to a `Bitmap` beginning at the first byte and ending at the last. - Bitmap folderIconBitmap = BitmapFactory.decodeByteArray(folderIconByteArray, 0, folderIconByteArray.length); - // Display `folderIconBitmap` in `move_to_folder_icon`. - ImageView folderIconImageView = view.findViewById(R.id.move_to_folder_icon); - folderIconImageView.setImageBitmap(folderIconBitmap); - - // Get the folder name from `cursor` and display it in `move_to_folder_name_textview`. - String folderName = cursor.getString(cursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME)); - TextView folderNameTextView = view.findViewById(R.id.move_to_folder_name_textview); - folderNameTextView.setText(folderName); - } - }; - } else { // Display `Home Folder` at the top of the `ListView`. - // Get the home folder icon drawable and convert it to a `Bitmap`. - Drawable homeFolderIconDrawable = ContextCompat.getDrawable(getActivity().getApplicationContext(), R.drawable.folder_gray_bitmap); - BitmapDrawable homeFolderIconBitmapDrawable = (BitmapDrawable) homeFolderIconDrawable; - assert homeFolderIconDrawable != null; - Bitmap homeFolderIconBitmap = homeFolderIconBitmapDrawable.getBitmap(); - - // Convert the folder `Bitmap` to a byte array. `0` is for lossless compression (the only option for a PNG). - ByteArrayOutputStream homeFolderIconByteArrayOutputStream = new ByteArrayOutputStream(); - homeFolderIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, homeFolderIconByteArrayOutputStream); - byte[] homeFolderIconByteArray = homeFolderIconByteArrayOutputStream.toByteArray(); - - // Setup a `MatrixCursor` for the `Home Folder`. - String[] homeFolderMatrixCursorColumnNames = {BookmarksDatabaseHelper._ID, BookmarksDatabaseHelper.BOOKMARK_NAME, BookmarksDatabaseHelper.FAVORITE_ICON}; - MatrixCursor homeFolderMatrixCursor = new MatrixCursor(homeFolderMatrixCursorColumnNames); - homeFolderMatrixCursor.addRow(new Object[]{0, getString(R.string.home_folder), homeFolderIconByteArray}); - - // Add the parent folder to the list of folders not to display. - exceptFolders.append(DatabaseUtils.sqlEscapeString(BookmarksActivity.currentFolder)); - - // If a folder is selected, add it and all children to the list of folders not to display. - long[] selectedBookmarksLongArray = BookmarksActivity.checkedItemIds; - for (long databaseIdLong : selectedBookmarksLongArray) { - // Get `databaseIdInt` for each selected bookmark. - int databaseIdInt = (int) databaseIdLong; - - // If `databaseIdInt` is a folder. - if (bookmarksDatabaseHelper.isFolder(databaseIdInt)) { - // Get the name of the selected folder. - String folderName = bookmarksDatabaseHelper.getFolderName(databaseIdInt); - - // Add the selected folder to the end of the list of folders not to display. - exceptFolders.append(","); - exceptFolders.append(DatabaseUtils.sqlEscapeString(folderName)); - - // Add the selected folder's subfolders to the list of folders not to display. - addSubfoldersToExceptFolders(folderName); - } - } - - // Get a `Cursor` containing the folders to display. - foldersCursor = bookmarksDatabaseHelper.getFoldersExcept(exceptFolders.toString()); - - // Combine `homeFolderMatrixCursor` and `foldersCursor`. - MergeCursor foldersMergeCursor = new MergeCursor(new Cursor[]{homeFolderMatrixCursor, foldersCursor}); - - // Setup `foldersCursorAdaptor`. `false` disables autoRequery. - foldersCursorAdapter = new CursorAdapter(alertDialog.getContext(), foldersMergeCursor, false) { - @Override - public View newView(Context context, Cursor cursor, ViewGroup parent) { - // Remove the incorrect lint warning that `.getLayoutInflater()` might be false. - assert getActivity() != null; - - // Inflate the individual item layout. `false` does not attach it to the root. - return getActivity().getLayoutInflater().inflate(R.layout.move_to_folder_item_linearlayout, parent, false); - } - - @Override - public void bindView(View view, Context context, Cursor cursor) { - // Get the folder icon from `cursor`. - byte[] folderIconByteArray = cursor.getBlob(cursor.getColumnIndex(BookmarksDatabaseHelper.FAVORITE_ICON)); - // Convert the byte array to a `Bitmap` beginning at the first byte and ending at the last. - Bitmap folderIconBitmap = BitmapFactory.decodeByteArray(folderIconByteArray, 0, folderIconByteArray.length); - // Display `folderIconBitmap` in `move_to_folder_icon`. - ImageView folderIconImageView = view.findViewById(R.id.move_to_folder_icon); - folderIconImageView.setImageBitmap(folderIconBitmap); - - // Get the folder name from `cursor` and display it in `move_to_folder_name_textview`. - String folderName = cursor.getString(cursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME)); - TextView folderNameTextView = view.findViewById(R.id.move_to_folder_name_textview); - folderNameTextView.setText(folderName); - } - }; - } - - // Get a handle for the folders list view. - ListView foldersListView = alertDialog.findViewById(R.id.move_to_folder_listview); - - // Remove the incorrect lint warning below that the view might be null. - assert foldersListView != null; - - // Set the folder list view adapter. - foldersListView.setAdapter(foldersCursorAdapter); - - // Enable the move button when a folder is selected. - foldersListView.setOnItemClickListener((AdapterView parent, View view, int position, long id) -> { - // Enable the move button. - moveButton.setEnabled(true); - }); - - // `onCreateDialog` requires the return of an `AlertDialog`. - return alertDialog; - } - - private void addSubfoldersToExceptFolders(String folderName) { - // Get a `Cursor` will all the immediate subfolders. - Cursor subfoldersCursor = bookmarksDatabaseHelper.getSubfolders(folderName); - - for (int i = 0; i < subfoldersCursor.getCount(); i++) { - // Move `subfolderCursor` to the current item. - subfoldersCursor.moveToPosition(i); - - // Get the name of the subfolder. - String subfolderName = subfoldersCursor.getString(subfoldersCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME)); - - // Add the subfolder to `exceptFolders`. - exceptFolders.append(","); - exceptFolders.append(DatabaseUtils.sqlEscapeString(subfolderName)); - - // Run the same tasks for any subfolders of the subfolder. - addSubfoldersToExceptFolders(subfolderName); - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/MoveToFolderDialog.kt b/app/src/main/java/com/stoutner/privacybrowser/dialogs/MoveToFolderDialog.kt new file mode 100644 index 00000000..84bb7478 --- /dev/null +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/MoveToFolderDialog.kt @@ -0,0 +1,325 @@ +/* + * Copyright © 2016-2021 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.annotation.SuppressLint +import android.app.Dialog +import android.content.Context +import android.content.DialogInterface +import android.content.res.Configuration +import android.database.Cursor +import android.database.DatabaseUtils +import android.database.MatrixCursor +import android.database.MergeCursor +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.graphics.drawable.BitmapDrawable +import android.os.Bundle +import android.view.View +import android.view.ViewGroup +import android.view.WindowManager +import android.widget.AdapterView +import android.widget.AdapterView.OnItemClickListener +import android.widget.ImageView +import android.widget.ListView +import android.widget.TextView + +import androidx.appcompat.app.AlertDialog +import androidx.core.content.ContextCompat +import androidx.cursoradapter.widget.CursorAdapter +import androidx.fragment.app.DialogFragment +import androidx.preference.PreferenceManager + +import com.stoutner.privacybrowser.R +import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper + +import java.io.ByteArrayOutputStream +import java.lang.StringBuilder + +// Declare the class constants. +private const val CURRENT_FOLDER = "current_folder" +private const val SELECTED_BOOKMARKS_LONG_ARRAY = "selected_bookmarks_long_array" + +class MoveToFolderDialog : DialogFragment() { + // Declare the class variables. + private lateinit var moveToFolderListener: MoveToFolderListener + private lateinit var bookmarksDatabaseHelper: BookmarksDatabaseHelper + private lateinit var exceptFolders: StringBuilder + + // The public interface is used to send information back to the parent activity. + interface MoveToFolderListener { + fun onMoveToFolder(dialogFragment: DialogFragment) + } + + override fun onAttach(context: Context) { + // Run the default commands. + super.onAttach(context) + + // Get a handle for the move to folder listener from the launching context. + moveToFolderListener = context as MoveToFolderListener + } + + companion object { + // `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin. + @JvmStatic + fun moveBookmarks(currentFolder: String, selectedBookmarksLongArray: LongArray): MoveToFolderDialog { + // Create an arguments bundle. + val argumentsBundle = Bundle() + + // Store the arguments in the bundle. + argumentsBundle.putString(CURRENT_FOLDER, currentFolder) + argumentsBundle.putLongArray(SELECTED_BOOKMARKS_LONG_ARRAY, selectedBookmarksLongArray) + + // Create a new instance of the dialog. + val moveToFolderDialog = MoveToFolderDialog() + + // And the bundle to the dialog. + moveToFolderDialog.arguments = argumentsBundle + + // Return the new dialog. + return moveToFolderDialog + } + } + + // `@SuppressLint("InflateParams")` removes the warning about using `null` as the parent view group when inflating the alert dialog. + @SuppressLint("InflateParams") + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + // Get the data from the arguments. + val currentFolder = requireArguments().getString(CURRENT_FOLDER)!! + val selectedBookmarksLongArray = requireArguments().getLongArray(SELECTED_BOOKMARKS_LONG_ARRAY)!! + + // Initialize the database helper. The `0` specifies a database version, but that is ignored and set instead using a constant in the bookmarks database helper. + bookmarksDatabaseHelper = BookmarksDatabaseHelper(context, null, null, 0) + + // Use an alert dialog builder to create the alert dialog. + val dialogBuilder = AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog) + + // Get the current theme status. + val currentThemeStatus = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK + + // Set the icon according to the theme. + if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { + dialogBuilder.setIcon(R.drawable.move_to_folder_blue_day) + } else { + dialogBuilder.setIcon(R.drawable.move_to_folder_blue_night) + } + + // Set the title. + dialogBuilder.setTitle(R.string.move_to_folder) + + // Set the view. The parent view is `null` because it will be assigned by the alert dialog. + dialogBuilder.setView(requireActivity().layoutInflater.inflate(R.layout.move_to_folder_dialog, null)) + + // Set the listener for the cancel button. Using `null` as the listener closes the dialog without doing anything else. + dialogBuilder.setNegativeButton(R.string.cancel, null) + + // Set the listener fo the move button. + dialogBuilder.setPositiveButton(R.string.move) { _: DialogInterface?, _: Int -> + // Return the dialog fragment to the parent activity on move. + moveToFolderListener.onMoveToFolder(this) + } + + // Create an alert dialog from the alert dialog builder. + val alertDialog = dialogBuilder.create() + + // Get a handle for the shared preferences. + val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) + + // Get the screenshot preference. + val allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false) + + // Disable screenshots if not allowed. + if (!allowScreenshots) { + // Disable screenshots. + alertDialog.window!!.addFlags(WindowManager.LayoutParams.FLAG_SECURE) + } + + // The alert dialog must be shown before items in the layout can be modified. + alertDialog.show() + + // Get a handle for the positive button. + val moveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE) + + // Initially disable the positive button. + moveButton.isEnabled = false + + // Initialize the except folders string builder. + exceptFolders = StringBuilder() + + // Declare the cursor variables. + val foldersCursor: Cursor + val foldersCursorAdapter: CursorAdapter + + // Check to see if the bookmark is currently in the home folder. + if (currentFolder.isEmpty()) { // The bookmark is currently in the home folder. Don't display `Home Folder` at the top of the list view. + // If a folder is selected, add it and all children to the list of folders not to display. + for (databaseIdLong in selectedBookmarksLongArray) { + // Get the database ID int for each selected bookmark. + val databaseIdInt = databaseIdLong.toInt() + + // Check to see if the bookmark is a folder. + if (bookmarksDatabaseHelper.isFolder(databaseIdInt)) { + // Add the folder to the list of folders not to display. + addFolderToExceptFolders(databaseIdInt) + } + } + + // Get a cursor containing the folders to display. + foldersCursor = bookmarksDatabaseHelper.getFoldersExcept(exceptFolders.toString()) + + // Populate the folders cursor adapter. + foldersCursorAdapter = populateFoldersCursorAdapter(requireContext(), foldersCursor) + } else { // The current folder is not directly in the home folder. Display `Home Folder` at the top of the list view. + // Get the home folder icon drawable. + val homeFolderIconDrawable = ContextCompat.getDrawable(requireActivity().applicationContext, R.drawable.folder_gray_bitmap) + + // Convert the home folder icon drawable to a bitmap drawable. + val homeFolderIconBitmapDrawable = homeFolderIconDrawable as BitmapDrawable + + // Convert the home folder bitmap drawable to a bitmap. + val homeFolderIconBitmap = homeFolderIconBitmapDrawable.bitmap + + // Create a home folder icon byte array output stream. + val homeFolderIconByteArrayOutputStream = ByteArrayOutputStream() + + // Convert the home folder bitmap to a byte array. `0` is for lossless compression (the only option for a PNG). + homeFolderIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, homeFolderIconByteArrayOutputStream) + + // Convert the home folder icon byte array output stream to a byte array. + val homeFolderIconByteArray = homeFolderIconByteArrayOutputStream.toByteArray() + + // Setup the home folder matrix cursor column names. + val homeFolderMatrixCursorColumnNames = arrayOf(BookmarksDatabaseHelper._ID, BookmarksDatabaseHelper.BOOKMARK_NAME, BookmarksDatabaseHelper.FAVORITE_ICON) + + // Setup a matrix cursor for the `Home Folder`. + val homeFolderMatrixCursor = MatrixCursor(homeFolderMatrixCursorColumnNames) + + // Add the home folder to the home folder matrix cursor. + homeFolderMatrixCursor.addRow(arrayOf(0, getString(R.string.home_folder), homeFolderIconByteArray)) + + // Add the parent folder to the list of folders not to display. + exceptFolders.append(DatabaseUtils.sqlEscapeString(currentFolder)) + + // If a folder is selected, add it and all children to the list of folders not to display. + for (databaseIdLong in selectedBookmarksLongArray) { + // Get the database ID int for each selected bookmark. + val databaseIdInt = databaseIdLong.toInt() + + // Check to see if the bookmark is a folder. + if (bookmarksDatabaseHelper.isFolder(databaseIdInt)) { + // Add the folder to the list of folders not to display. + addFolderToExceptFolders(databaseIdInt) + } + } + + // Get a cursor containing the folders to display. + foldersCursor = bookmarksDatabaseHelper.getFoldersExcept(exceptFolders.toString()) + + // Combine the home folder matrix cursor and the folders cursor. + val foldersMergeCursor = MergeCursor(arrayOf(homeFolderMatrixCursor, foldersCursor)) + + // Populate the folders cursor adapter. + foldersCursorAdapter = populateFoldersCursorAdapter(requireContext(), foldersMergeCursor) + } + + // Get a handle for the folders list view. + val foldersListView = alertDialog.findViewById(R.id.move_to_folder_listview)!! + + // Set the folder list view adapter. + foldersListView.adapter = foldersCursorAdapter + + // Enable the move button when a folder is selected. + foldersListView.onItemClickListener = OnItemClickListener { _: AdapterView<*>?, _: View?, _: Int, _: Long -> + // Enable the move button. + moveButton.isEnabled = true + } + + // Return the alert dialog. + return alertDialog + } + + private fun addFolderToExceptFolders(databaseIdInt: Int) { + // Get the name of the selected folder. + val folderName = bookmarksDatabaseHelper.getFolderName(databaseIdInt) + + // Populate the list of folders not to get. + if (exceptFolders.isEmpty()) { + // Add the selected folder to the list of folders not to display. + exceptFolders.append(DatabaseUtils.sqlEscapeString(folderName)) + } else { + // Add the selected folder to the end of the list of folders not to display. + exceptFolders.append(",") + exceptFolders.append(DatabaseUtils.sqlEscapeString(folderName)) + } + + // Add the selected folder's subfolders to the list of folders not to display. + addSubfoldersToExceptFolders(folderName) + } + + private fun addSubfoldersToExceptFolders(folderName: String) { + // Get a cursor with all the immediate subfolders. + val subfoldersCursor = bookmarksDatabaseHelper.getSubfolders(folderName) + + // Add each subfolder to the list of folders not to display. + for (i in 0 until subfoldersCursor.count) { + // Move the subfolder cursor to the current item. + subfoldersCursor.moveToPosition(i) + + // Get the name of the subfolder. + val subfolderName = subfoldersCursor.getString(subfoldersCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME)) + + // Add the subfolder to except folders. + exceptFolders.append(",") + exceptFolders.append(DatabaseUtils.sqlEscapeString(subfolderName)) + + // Run the same tasks for any subfolders of the subfolder. + addSubfoldersToExceptFolders(subfolderName) + } + } + + private fun populateFoldersCursorAdapter(context: Context, cursor: Cursor): CursorAdapter { + // Return the folders cursor adapter. + return object : CursorAdapter(context, cursor, false) { + override fun newView(context: Context, cursor: Cursor, parent: ViewGroup): View { + // Inflate the individual item layout. + return requireActivity().layoutInflater.inflate(R.layout.move_to_folder_item_linearlayout, parent, false) + } + + override fun bindView(view: View, context: Context, cursor: Cursor) { + // Get the data from the cursor. + val folderIconByteArray = cursor.getBlob(cursor.getColumnIndex(BookmarksDatabaseHelper.FAVORITE_ICON)) + val folderName = cursor.getString(cursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME)) + + // Get handles for the views. + val folderIconImageView = view.findViewById(R.id.move_to_folder_icon) + val folderNameTextView = view.findViewById(R.id.move_to_folder_name_textview) + + // Convert the byte array to a bitmap beginning at the first byte and ending at the last. + val folderIconBitmap = BitmapFactory.decodeByteArray(folderIconByteArray, 0, folderIconByteArray.size) + + // Display the folder icon bitmap. + folderIconImageView.setImageBitmap(folderIconBitmap) + + // Display the folder name. + folderNameTextView.text = folderName + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/OpenDialog.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/OpenDialog.java deleted file mode 100644 index 43594860..00000000 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/OpenDialog.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright © 2019-2020 Soren Stoutner . - * - * This file is part of Privacy Browser . - * - * Privacy Browser is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Privacy Browser is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Privacy Browser. If not, see . - */ - -package com.stoutner.privacybrowser.dialogs; - -import android.Manifest; -import android.annotation.SuppressLint; -import android.app.Activity; -import android.app.Dialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.content.res.Configuration; -import android.os.Build; -import android.os.Bundle; -import android.os.Environment; -import android.provider.DocumentsContract; -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 androidx.annotation.NonNull; -import androidx.appcompat.app.AlertDialog; -import androidx.core.content.ContextCompat; -import androidx.fragment.app.DialogFragment; -import androidx.preference.PreferenceManager; - -import com.stoutner.privacybrowser.R; -import com.stoutner.privacybrowser.activities.MainWebViewActivity; -import com.stoutner.privacybrowser.helpers.DownloadLocationHelper; - -import java.io.File; - -public class OpenDialog extends DialogFragment { - // Define the open listener. - private OpenListener openListener; - - // The public interface is used to send information back to the parent activity. - public interface OpenListener { - void onOpen(DialogFragment dialogFragment); - } - - @Override - public void onAttach(@NonNull Context context) { - // Run the default commands. - super.onAttach(context); - - // Get a handle for the open listener from the launching context. - openListener = (OpenListener) context; - } - - // `@SuppressLint("InflateParams")` removes the warning about using null as the parent view group when inflating the alert dialog. - @SuppressLint("InflateParams") - @Override - @NonNull - public Dialog onCreateDialog(Bundle savedInstanceState) { - // Get a handle for the activity and the context. - Activity activity = requireActivity(); - Context context = requireContext(); - - // Use an alert dialog builder to create the alert dialog. - AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context, R.style.PrivacyBrowserAlertDialog); - - // Get the current theme status. - int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; - - // Set the icon according to the theme. - if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) { - dialogBuilder.setIcon(R.drawable.proxy_enabled_night); - } else { - dialogBuilder.setIcon(R.drawable.proxy_enabled_day); - } - - // Set the title. - dialogBuilder.setTitle(R.string.open); - - // Set the view. The parent view is null because it will be assigned by the alert dialog. - dialogBuilder.setView(activity.getLayoutInflater().inflate(R.layout.open_dialog, null)); - - // Set the cancel button listener. Using `null` as the listener closes the dialog without doing anything else. - dialogBuilder.setNegativeButton(R.string.cancel, null); - - // Set the open button listener. - dialogBuilder.setPositiveButton(R.string.open, (DialogInterface dialog, int which) -> { - // Return the dialog fragment to the parent activity. - openListener.onOpen(this); - }); - - // Create an alert dialog from the builder. - AlertDialog alertDialog = dialogBuilder.create(); - - // Remove the incorrect lint warning below that the window might be null. - assert alertDialog.getWindow() != null; - - // Get a handle for the shared preferences. - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); - - // Get the screenshot preference. - boolean allowScreenshots = sharedPreferences.getBoolean("allow_screenshots", false); - - // Disable screenshots if not allowed. - if (!allowScreenshots) { - alertDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE); - } - - // The alert dialog must be shown before items in the layout can be modified. - alertDialog.show(); - - // Get handles for the layout items. - EditText fileNameEditText = alertDialog.findViewById(R.id.file_name_edittext); - Button browseButton = alertDialog.findViewById(R.id.browse_button); - TextView fileDoesNotExistTextView = alertDialog.findViewById(R.id.file_does_not_exist_textview); - TextView storagePermissionTextView = alertDialog.findViewById(R.id.storage_permission_textview); - Button openButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE); - - // Remove the incorrect lint warnings below that the views might be null. - assert fileNameEditText != null; - assert browseButton != null; - assert fileDoesNotExistTextView != null; - assert storagePermissionTextView != null; - - // Update the status of the open button when the file name changes. - fileNameEditText.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { - // Do nothing. - } - - @Override - public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { - // Do nothing. - } - - @Override - public void afterTextChanged(Editable editable) { - // Get the current file name. - String fileNameString = fileNameEditText.getText().toString(); - - // Convert the file name string to a file. - File file = new File(fileNameString); - - // Check to see if the file exists. - if (file.exists()) { // The file exists. - // Hide the notification that the file does not exist. - fileDoesNotExistTextView.setVisibility(View.GONE); - - // Enable the open button. - openButton.setEnabled(true); - } else { // The file does not exist. - // Show the notification that the file does not exist. - fileDoesNotExistTextView.setVisibility(View.VISIBLE); - - // Disable the open button. - openButton.setEnabled(false); - } - } - }); - - // Instantiate the download location helper. - DownloadLocationHelper downloadLocationHelper = new DownloadLocationHelper(); - - // Get the default file path. - String defaultFilePath = downloadLocationHelper.getDownloadLocation(context) + "/"; - - // Display the default file path. - fileNameEditText.setText(defaultFilePath); - - // Move the cursor to the end of the default file path. - fileNameEditText.setSelection(defaultFilePath.length()); - - // Hide the storage permission text view if the permission has already been granted. - if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { - storagePermissionTextView.setVisibility(View.GONE); - } - - // Handle clicks on the browse button. - browseButton.setOnClickListener((View view) -> { - // Create the file picker intent. - Intent browseIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT); - - // Set the intent MIME type to include all files so that everything is visible. - browseIntent.setType("*/*"); - - // Set the initial directory if the minimum API >= 26. - if (Build.VERSION.SDK_INT >= 26) { - browseIntent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, Environment.getExternalStorageDirectory()); - } - - // Start the file picker. This must be started under `activity` to that the request code is returned correctly. - activity.startActivityForResult(browseIntent, MainWebViewActivity.BROWSE_OPEN_REQUEST_CODE); - }); - - // Return the alert dialog. - return alertDialog; - } -} diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/OpenDialog.kt b/app/src/main/java/com/stoutner/privacybrowser/dialogs/OpenDialog.kt new file mode 100644 index 00000000..123c6d30 --- /dev/null +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/OpenDialog.kt @@ -0,0 +1,188 @@ +/* + * Copyright © 2019-2021 Soren Stoutner . + * + * This file is part of Privacy Browser . + * + * Privacy Browser is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Privacy Browser is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Privacy Browser. If not, see . + */ + +package com.stoutner.privacybrowser.dialogs + +import android.Manifest +import android.annotation.SuppressLint +import android.app.Dialog +import android.content.Context +import android.content.DialogInterface +import android.content.Intent +import android.content.pm.PackageManager +import android.content.res.Configuration +import android.os.Bundle +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 androidx.appcompat.app.AlertDialog +import androidx.core.content.ContextCompat +import androidx.fragment.app.DialogFragment +import androidx.preference.PreferenceManager + +import com.stoutner.privacybrowser.R +import com.stoutner.privacybrowser.activities.MainWebViewActivity +import com.stoutner.privacybrowser.helpers.DownloadLocationHelper + +import java.io.File + +class OpenDialog : DialogFragment() { + // Define the open listener. + private lateinit var openListener: OpenListener + + // The public interface is used to send information back to the parent activity. + interface OpenListener { + fun onOpen(dialogFragment: DialogFragment) + } + + override fun onAttach(context: Context) { + // Run the default commands. + super.onAttach(context) + + // Get a handle for the open listener from the launching context. + openListener = context as OpenListener + } + + // `@SuppressLint("InflateParams")` removes the warning about using null as the parent view group when inflating the alert dialog. + @SuppressLint("InflateParams") + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + // Use an alert dialog builder to create the alert dialog. + val dialogBuilder = AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog) + + // Get the current theme status. + val currentThemeStatus = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK + + // Set the icon according to the theme. + if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { + dialogBuilder.setIcon(R.drawable.proxy_enabled_day) + } else { + dialogBuilder.setIcon(R.drawable.proxy_enabled_night) + } + + // Set the title. + dialogBuilder.setTitle(R.string.open) + + // Set the view. The parent view is null because it will be assigned by the alert dialog. + dialogBuilder.setView(requireActivity().layoutInflater.inflate(R.layout.open_dialog, null)) + + // Set the cancel button listener. Using `null` as the listener closes the dialog without doing anything else. + dialogBuilder.setNegativeButton(R.string.cancel, null) + + // Set the open button listener. + dialogBuilder.setPositiveButton(R.string.open) { _: DialogInterface?, _: Int -> + // Return the dialog fragment to the parent activity. + openListener.onOpen(this) + } + + // Create an alert dialog from the builder. + val alertDialog = dialogBuilder.create() + + // Get a handle for the shared preferences. + val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) + + // Get the screenshot preference. + val allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false) + + // Disable screenshots if not allowed. + if (!allowScreenshots) { + alertDialog.window!!.addFlags(WindowManager.LayoutParams.FLAG_SECURE) + } + + // The alert dialog must be shown before items in the layout can be modified. + alertDialog.show() + + // Get handles for the layout items. + val fileNameEditText = alertDialog.findViewById(R.id.file_name_edittext)!! + val browseButton = alertDialog.findViewById