/*
- * Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2016-2021 Soren Stoutner <soren@stoutner.com>.
*
* This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
*
// 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);
/*
- * Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2016-2021 Soren Stoutner <soren@stoutner.com>.
*
* This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
*
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;
// 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));
}
@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();
/*
- * Copyright © 2015-2020 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2015-2021 Soren Stoutner <soren@stoutner.com>.
*
* Download cookie code contributed 2017 Hendrik Knackstedt. Copyright assigned to Soren Stoutner <soren@stoutner.com>.
*
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++) {
}
}
+ // `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.
}
@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;
+++ /dev/null
-/*
- * Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
- *
- * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
- *
- * Privacy Browser is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Privacy Browser is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.stoutner.privacybrowser.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<Fragment> 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
--- /dev/null
+/*
+ * Copyright © 2016-2021 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+ *
+ * Privacy Browser is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Privacy Browser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.stoutner.privacybrowser.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<String>) :
+ FragmentPagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
+ // Define the class variables.
+ private val aboutFragmentList = LinkedList<Fragment>()
+
+ // 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
--- /dev/null
+/*
+ * Copyright © 2021 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+ *
+ * Privacy Browser is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Privacy Browser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.stoutner.privacybrowser.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<NestedScrollWebView>(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<TextView>(R.id.domain_name)
+ val ipAddressesTextView = tabLayout.findViewById<TextView>(R.id.ip_addresses)
+ val issuedToCNameTextView = tabLayout.findViewById<TextView>(R.id.issued_to_cname)
+ val issuedToONameTextView = tabLayout.findViewById<TextView>(R.id.issued_to_oname)
+ val issuedToUNameTextView = tabLayout.findViewById<TextView>(R.id.issued_to_uname)
+ val issuedByCNameTextView = tabLayout.findViewById<TextView>(R.id.issued_by_cname)
+ val issuedByONameTextView = tabLayout.findViewById<TextView>(R.id.issued_by_oname)
+ val issuedByUNameTextView = tabLayout.findViewById<TextView>(R.id.issued_by_uname)
+ val startDateTextView = tabLayout.findViewById<TextView>(R.id.start_date)
+ val endDateTextView = tabLayout.findViewById<TextView>(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
/*
- * Copyright © 2018-2020 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2018-2021 Soren Stoutner <soren@stoutner.com>.
*
* This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
*
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)
/*
- * Copyright © 2017-2020 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2017-2021 Soren Stoutner <soren@stoutner.com>.
*
* This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
*
// 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)
}
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.
// 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)
/*
- * Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2016-2021 Soren Stoutner <soren@stoutner.com>.
*
* This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
*
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)
}
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.
/*
- * Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2016-2021 Soren Stoutner <soren@stoutner.com>.
*
* This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
*
// 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)
}
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.
/*
- * Copyright © 2015-2020 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2015-2021 Soren Stoutner <soren@stoutner.com>.
*
* This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
*
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.
/*
- * Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2016-2021 Soren Stoutner <soren@stoutner.com>.
*
* This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
*
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
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)
}
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.
/*
- * Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2016-2021 Soren Stoutner <soren@stoutner.com>.
*
* This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
*
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
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)
}
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.
/*
- * Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2016-2021 Soren Stoutner <soren@stoutner.com>.
*
* This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
*
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
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)
}
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.
/*
- * Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2016-2021 Soren Stoutner <soren@stoutner.com>.
*
* This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
*
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
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)
}
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.
/*
- * Copyright © 2019-2020 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2019-2021 Soren Stoutner <soren@stoutner.com>.
*
* This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
*
// 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)
}
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.
/*
- * Copyright © 2017-2020 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2017-2021 Soren Stoutner <soren@stoutner.com>.
*
* This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
*
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
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.
+++ /dev/null
-/*
- * Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
- *
- * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
- *
- * Privacy Browser is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Privacy Browser is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.stoutner.privacybrowser.dialogs;
-
-import android.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
--- /dev/null
+/*
+ * Copyright © 2016-2021 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+ *
+ * Privacy Browser is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Privacy Browser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.stoutner.privacybrowser.dialogs
+
+import android.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<Any>(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<ListView>(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<ImageView>(R.id.move_to_folder_icon)
+ val folderNameTextView = view.findViewById<TextView>(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
+++ /dev/null
-/*
- * Copyright © 2019-2020 Soren Stoutner <soren@stoutner.com>.
- *
- * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
- *
- * Privacy Browser is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Privacy Browser is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.stoutner.privacybrowser.dialogs;
-
-import android.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;
- }
-}
--- /dev/null
+/*
+ * Copyright © 2019-2021 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+ *
+ * Privacy Browser is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Privacy Browser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.stoutner.privacybrowser.dialogs
+
+import android.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<EditText>(R.id.file_name_edittext)!!
+ val browseButton = alertDialog.findViewById<Button>(R.id.browse_button)!!
+ val fileDoesNotExistTextView = alertDialog.findViewById<TextView>(R.id.file_does_not_exist_textview)!!
+ val storagePermissionTextView = alertDialog.findViewById<TextView>(R.id.storage_permission_textview)!!
+ val openButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE)
+
+ // Update the status of the open button when the file name changes.
+ fileNameEditText.addTextChangedListener(object : TextWatcher {
+ override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
+ // Do nothing.
+ }
+
+ override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
+ // Do nothing.
+ }
+
+ override fun afterTextChanged(editable: Editable) {
+ // Get the current file name.
+ val fileNameString = fileNameEditText.text.toString()
+
+ // Convert the file name string to a file.
+ val file = 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.visibility = View.GONE
+
+ // Enable the open button.
+ openButton.isEnabled = true
+ } else { // The file does not exist.
+ // Show the notification that the file does not exist.
+ fileDoesNotExistTextView.visibility = View.VISIBLE
+
+ // Disable the open button.
+ openButton.isEnabled = false
+ }
+ }
+ })
+
+ // Instantiate the download location helper.
+ val downloadLocationHelper = DownloadLocationHelper()
+
+ // Get the default file path.
+ val 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(requireContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
+ storagePermissionTextView.visibility = View.GONE
+ }
+
+ // Handle clicks on the browse button.
+ browseButton.setOnClickListener {
+ // Create the file picker intent.
+ val browseIntent = Intent(Intent.ACTION_OPEN_DOCUMENT)
+
+ // Set the intent MIME type to include all files so that everything is visible.
+ browseIntent.type = "*/*"
+
+ // Start the file picker. This must be started under `activity` to that the request code is returned correctly.
+ requireActivity().startActivityForResult(browseIntent, MainWebViewActivity.BROWSE_OPEN_REQUEST_CODE)
+ }
+
+ // Return the alert dialog.
+ return alertDialog
+ }
+}
\ No newline at end of file
+++ /dev/null
-/*
- * Copyright © 2017-2020 Soren Stoutner <soren@stoutner.com>.
- *
- * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
- *
- * Privacy Browser is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Privacy Browser is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.stoutner.privacybrowser.dialogs;
-
-import android.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.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.net.http.SslCertificate;
-import android.os.Bundle;
-import android.preference.PreferenceManager;
-import android.text.SpannableStringBuilder;
-import android.text.Spanned;
-import android.text.style.ForegroundColorSpan;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.TextView;
-
-import com.google.android.material.tabs.TabLayout;
-
-import com.stoutner.privacybrowser.R;
-import com.stoutner.privacybrowser.activities.MainWebViewActivity;
-import com.stoutner.privacybrowser.fragments.WebViewTabFragment;
-import com.stoutner.privacybrowser.views.NestedScrollWebView;
-import com.stoutner.privacybrowser.views.WrapVerticalContentViewPager;
-import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper;
-
-import java.text.DateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-
-import androidx.annotation.NonNull;
-import androidx.appcompat.app.AlertDialog;
-import androidx.core.content.ContextCompat;
-import androidx.fragment.app.DialogFragment; // The AndroidX dialog fragment must be used or an error is produced on API <=22.
-import androidx.viewpager.widget.PagerAdapter;
-
-public class PinnedMismatchDialog extends DialogFragment {
- // The public interface is used to send information back to the parent activity.
- public interface PinnedMismatchListener {
- void pinnedErrorGoBack();
- }
-
- // Declare the class variables.
- private PinnedMismatchListener pinnedMismatchListener;
- private NestedScrollWebView nestedScrollWebView;
- private String currentSslIssuedToCName;
- private String currentSslIssuedToOName;
- private String currentSslIssuedToUName;
- private String currentSslIssuedByCName;
- private String currentSslIssuedByOName;
- private String currentSslIssuedByUName;
- private Date currentSslStartDate;
- private Date currentSslEndDate;
-
- @Override
- public void onAttach(@NonNull Context context) {
- // Run the default commands.
- super.onAttach(context);
-
- // Get a handle for the listener from the launching context.
- pinnedMismatchListener = (PinnedMismatchListener) context;
- }
-
- public static PinnedMismatchDialog displayDialog(long webViewFragmentId) {
- // Create an arguments bundle.
- Bundle argumentsBundle = new Bundle();
-
- // Store the WebView fragment ID in the bundle.
- argumentsBundle.putLong("webview_fragment_id", webViewFragmentId);
-
- // Create a new instance of the pinned mismatch dialog.
- PinnedMismatchDialog pinnedMismatchDialog = new PinnedMismatchDialog();
-
- // Add the arguments bundle to the new instance.
- pinnedMismatchDialog.setArguments(argumentsBundle);
-
- // Make it so.
- return pinnedMismatchDialog;
- }
-
- // `@SuppressLint("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`.
- @SuppressLint("InflateParams")
- @Override
- @NonNull
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- // Get the arguments.
- Bundle arguments = getArguments();
-
- // Remove the incorrect lint warning below that `.getArguments().getInt()` might be null.
- assert arguments != null;
-
- // Get the current position of this WebView fragment.
- int webViewPosition = MainWebViewActivity.webViewPagerAdapter.getPositionForId(arguments.getLong("webview_fragment_id"));
-
- // Get the WebView tab fragment.
- WebViewTabFragment webViewTabFragment = MainWebViewActivity.webViewPagerAdapter.getPageFragment(webViewPosition);
-
- // Get the fragment view.
- View fragmentView = webViewTabFragment.getView();
-
- // Remove the incorrect lint warning below that the fragment view might be null.
- assert fragmentView != null;
-
- // Get a handle for the current WebView.
- nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
-
- // Use an alert dialog builder to create the alert dialog.
- AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog);
-
- // Get the context.
- Context context = getContext();
-
- // Remove the incorrect lint warning below that the context might be null.
- assert context != null;
-
- // Get the favorite icon.
- Bitmap favoriteIconBitmap = nestedScrollWebView.getFavoriteOrDefaultIcon();
-
- // Get the default favorite icon drawable. `ContextCompat` must be used until API >= 21.
- Drawable defaultFavoriteIconDrawable = ContextCompat.getDrawable(context, R.drawable.world);
-
- // Cast the favorite icon drawable to a bitmap drawable.
- BitmapDrawable defaultFavoriteIconBitmapDrawable = (BitmapDrawable) defaultFavoriteIconDrawable;
-
- // Remove the incorrect warning below that the favorite icon bitmap drawable might be null.
- assert defaultFavoriteIconBitmapDrawable != null;
-
- // Store the default icon bitmap.
- Bitmap defaultFavoriteIconBitmap = defaultFavoriteIconBitmapDrawable.getBitmap();
-
- // Set the favorite icon as the dialog icon if it exists.
- if (favoriteIconBitmap.sameAs(defaultFavoriteIconBitmap)) { // There is no website favorite icon.
- // 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.ssl_certificate_enabled_night);
- } else {
- dialogBuilder.setIcon(R.drawable.ssl_certificate_enabled_day);
- }
- } else { // There is a favorite icon.
- // Create a drawable version of the favorite icon.
- Drawable favoriteIconDrawable = new BitmapDrawable(getResources(), favoriteIconBitmap);
-
- // Set the icon.
- dialogBuilder.setIcon(favoriteIconDrawable);
- }
-
- // Setup the neutral button.
- dialogBuilder.setNeutralButton(R.string.update, (DialogInterface dialog, int which) -> {
- // Initialize the long date variables. If the date is null, a long value of `0` will be stored in the Domains database entry.
- long currentSslStartDateLong = 0;
- long currentSslEndDateLong = 0;
-
- // Convert the `Dates` into `longs`.
- if (currentSslStartDate != null) {
- currentSslStartDateLong = currentSslStartDate.getTime();
- }
-
- if (currentSslEndDate != null) {
- currentSslEndDateLong = currentSslEndDate.getTime();
- }
-
- // Initialize the database handler. The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`.
- DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(context, null, null, 0);
-
- // Update the SSL certificate if it is pinned.
- if (nestedScrollWebView.hasPinnedSslCertificate()) {
- // Update the pinned SSL certificate in the domain database.
- domainsDatabaseHelper.updatePinnedSslCertificate(nestedScrollWebView.getDomainSettingsDatabaseId(), currentSslIssuedToCName, currentSslIssuedToOName, currentSslIssuedToUName,
- currentSslIssuedByCName, currentSslIssuedByOName, currentSslIssuedByUName, currentSslStartDateLong, currentSslEndDateLong);
-
- // Update the pinned SSL certificate in the nested scroll WebView.
- nestedScrollWebView.setPinnedSslCertificate(currentSslIssuedToCName, currentSslIssuedToOName, currentSslIssuedToUName, currentSslIssuedByCName, currentSslIssuedByOName, currentSslIssuedByUName,
- currentSslStartDate, currentSslEndDate);
- }
-
- // Update the IP addresses if they are pinned.
- if (nestedScrollWebView.hasPinnedIpAddresses()) {
- // Update the pinned IP addresses in the domain database.
- domainsDatabaseHelper.updatePinnedIpAddresses(nestedScrollWebView.getDomainSettingsDatabaseId(), nestedScrollWebView.getCurrentIpAddresses());
-
- // Update the pinned IP addresses in the nested scroll WebView.
- nestedScrollWebView.setPinnedIpAddresses(nestedScrollWebView.getCurrentIpAddresses());
- }
- });
-
- // Setup the back button.
- dialogBuilder.setNegativeButton(R.string.back, (DialogInterface dialog, int which) -> {
- if (nestedScrollWebView.canGoBack()) { // There is a back page in the history.
- // Invoke the navigate history listener in the calling activity. These commands cannot be run here because they need access to `applyDomainSettings()`.
- pinnedMismatchListener.pinnedErrorGoBack();
- } else { // There are no pages to go back to.
- // Load a blank page
- nestedScrollWebView.loadUrl("");
- }
- });
-
- // Setup the proceed button.
- dialogBuilder.setPositiveButton(R.string.proceed, (DialogInterface dialog, int which) -> {
- // Do not check the pinned information for this domain again until the domain changes.
- nestedScrollWebView.setIgnorePinnedDomainInformation(true);
- });
-
- // Set the title.
- dialogBuilder.setTitle(R.string.pinned_mismatch);
-
- // Remove the incorrect lint warning below that `getLayoutInflater()` might be null.
- assert getActivity() != null;
-
- // Set the layout. The parent view is `null` because it will be assigned by `AlertDialog`.
- // For some reason, `getLayoutInflater()` without `getActivity()` produces an endless loop (probably a bug that will be fixed at some point in the future).
- dialogBuilder.setView(getActivity().getLayoutInflater().inflate(R.layout.pinned_mismatch_linearlayout, null));
-
- // 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 views.
- WrapVerticalContentViewPager wrapVerticalContentViewPager = alertDialog.findViewById(R.id.pinned_ssl_certificate_mismatch_viewpager);
- TabLayout tabLayout = alertDialog.findViewById(R.id.pinned_ssl_certificate_mismatch_tablayout);
-
- // Remove the incorrect lint warning below that the views might be null.
- assert wrapVerticalContentViewPager != null;
- assert tabLayout != null;
-
- // Set the view pager adapter.
- wrapVerticalContentViewPager.setAdapter(new pagerAdapter());
-
- // Connect the tab layout to the view pager.
- tabLayout.setupWithViewPager(wrapVerticalContentViewPager);
-
- // Return the alert dialog.
- return alertDialog;
- }
-
- private class pagerAdapter extends PagerAdapter {
- @Override
- public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
- // Check to see if the `View` and the `Object` are the same.
- return (view == object);
- }
-
- @Override
- public int getCount() {
- // There are two tabs.
- return 2;
- }
-
- @Override
- public CharSequence getPageTitle(int position) {
- // Return the current tab title.
- if (position == 0) { // The current SSL certificate tab.
- return getString(R.string.current);
- } else { // The pinned SSL certificate tab.
- return getString(R.string.pinned);
- }
- }
-
- @Override
- @NonNull
- public Object instantiateItem(@NonNull ViewGroup container, int position) {
- // Inflate the scroll view for this tab.
- ViewGroup tabViewGroup = (ViewGroup) getLayoutInflater().inflate(R.layout.pinned_mismatch_scrollview, container, false);
-
- // Get handles for the `TextViews`.
- TextView domainNameTextView = tabViewGroup.findViewById(R.id.domain_name);
- TextView ipAddressesTextView = tabViewGroup.findViewById(R.id.ip_addresses);
- TextView issuedToCNameTextView = tabViewGroup.findViewById(R.id.issued_to_cname);
- TextView issuedToONameTextView = tabViewGroup.findViewById(R.id.issued_to_oname);
- TextView issuedToUNameTextView = tabViewGroup.findViewById(R.id.issued_to_uname);
- TextView issuedByCNameTextView = tabViewGroup.findViewById(R.id.issued_by_cname);
- TextView issuedByONameTextView = tabViewGroup.findViewById(R.id.issued_by_oname);
- TextView issuedByUNameTextView = tabViewGroup.findViewById(R.id.issued_by_uname);
- TextView startDateTextView = tabViewGroup.findViewById(R.id.start_date);
- TextView endDateTextView = tabViewGroup.findViewById(R.id.end_date);
-
- // Setup the labels.
- String domainNameLabel = getString(R.string.domain_label) + " ";
- String ipAddressesLabel = getString(R.string.ip_addresses) + " ";
- String cNameLabel = getString(R.string.common_name) + " ";
- String oNameLabel = getString(R.string.organization) + " ";
- String uNameLabel = getString(R.string.organizational_unit) + " ";
- String startDateLabel = getString(R.string.start_date) + " ";
- String endDateLabel = getString(R.string.end_date) + " ";
-
- // Convert the URL to a URI.
- Uri currentUri = Uri.parse(nestedScrollWebView.getUrl());
-
- // Get the current host from the URI.
- String domainName = currentUri.getHost();
-
- // Get the current website SSL certificate.
- SslCertificate sslCertificate = nestedScrollWebView.getCertificate();
-
- // Extract the individual pieces of information from the current website SSL certificate if it is not null.
- if (sslCertificate != null) {
- currentSslIssuedToCName = sslCertificate.getIssuedTo().getCName();
- currentSslIssuedToOName = sslCertificate.getIssuedTo().getOName();
- currentSslIssuedToUName = sslCertificate.getIssuedTo().getUName();
- currentSslIssuedByCName = sslCertificate.getIssuedBy().getCName();
- currentSslIssuedByOName = sslCertificate.getIssuedBy().getOName();
- currentSslIssuedByUName = sslCertificate.getIssuedBy().getUName();
- currentSslStartDate = sslCertificate.getValidNotBeforeDate();
- currentSslEndDate = sslCertificate.getValidNotAfterDate();
- } else {
- // Initialize the current website SSL certificate variables with blank information.
- currentSslIssuedToCName = "";
- currentSslIssuedToOName = "";
- currentSslIssuedToUName = "";
- currentSslIssuedByCName = "";
- currentSslIssuedByOName = "";
- currentSslIssuedByUName = "";
- }
-
- // Get the pinned SSL certificate.
- ArrayList<Object> pinnedSslCertificateArrayList = nestedScrollWebView.getPinnedSslCertificate();
-
- // Extract the arrays from the array list.
- String[] pinnedSslCertificateStringArray = (String[]) pinnedSslCertificateArrayList.get(0);
- Date[] pinnedSslCertificateDateArray = (Date[]) pinnedSslCertificateArrayList.get(1);
-
- // Setup the domain name spannable string builder.
- SpannableStringBuilder domainNameStringBuilder = new SpannableStringBuilder(domainNameLabel + domainName);
-
- // Initialize the spannable string builders.
- SpannableStringBuilder ipAddressesStringBuilder;
- SpannableStringBuilder issuedToCNameStringBuilder;
- SpannableStringBuilder issuedToONameStringBuilder;
- SpannableStringBuilder issuedToUNameStringBuilder;
- SpannableStringBuilder issuedByCNameStringBuilder;
- SpannableStringBuilder issuedByONameStringBuilder;
- SpannableStringBuilder issuedByUNameStringBuilder;
- SpannableStringBuilder startDateStringBuilder;
- SpannableStringBuilder endDateStringBuilder;
-
- // Setup the spannable string builders for each tab.
- if (position == 0) { // Setup the current settings tab.
- // Create the string builders.
- ipAddressesStringBuilder = new SpannableStringBuilder(ipAddressesLabel + nestedScrollWebView.getCurrentIpAddresses());
- issuedToCNameStringBuilder = new SpannableStringBuilder(cNameLabel + currentSslIssuedToCName);
- issuedToONameStringBuilder = new SpannableStringBuilder(oNameLabel + currentSslIssuedToOName);
- issuedToUNameStringBuilder = new SpannableStringBuilder(uNameLabel + currentSslIssuedToUName);
- issuedByCNameStringBuilder = new SpannableStringBuilder(cNameLabel + currentSslIssuedByCName);
- issuedByONameStringBuilder = new SpannableStringBuilder(oNameLabel + currentSslIssuedByOName);
- issuedByUNameStringBuilder = new SpannableStringBuilder(uNameLabel + currentSslIssuedByUName);
-
- // Set the dates if they aren't `null`.
- if (currentSslStartDate == null) {
- startDateStringBuilder = new SpannableStringBuilder(startDateLabel);
- } else {
- startDateStringBuilder = new SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(currentSslStartDate));
- }
-
- if (currentSslEndDate == null) {
- endDateStringBuilder = new SpannableStringBuilder(endDateLabel);
- } else {
- endDateStringBuilder = new SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(currentSslEndDate));
- }
- } else { // Setup the pinned settings tab.
- // Create the string builders.
- ipAddressesStringBuilder = new SpannableStringBuilder(ipAddressesLabel + nestedScrollWebView.getPinnedIpAddresses());
- issuedToCNameStringBuilder = new SpannableStringBuilder(cNameLabel + pinnedSslCertificateStringArray[0]);
- issuedToONameStringBuilder = new SpannableStringBuilder(oNameLabel + pinnedSslCertificateStringArray[1]);
- issuedToUNameStringBuilder = new SpannableStringBuilder(uNameLabel + pinnedSslCertificateStringArray[2]);
- issuedByCNameStringBuilder = new SpannableStringBuilder(cNameLabel + pinnedSslCertificateStringArray[3]);
- issuedByONameStringBuilder = new SpannableStringBuilder(oNameLabel + pinnedSslCertificateStringArray[4]);
- issuedByUNameStringBuilder = new SpannableStringBuilder(uNameLabel + pinnedSslCertificateStringArray[5]);
-
- // Set the dates if they aren't `null`.
- if (pinnedSslCertificateDateArray[0] == null) {
- startDateStringBuilder = new SpannableStringBuilder(startDateLabel);
- } else {
- startDateStringBuilder = new SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(pinnedSslCertificateDateArray[0]));
- }
-
- if (pinnedSslCertificateDateArray[1] == null) {
- endDateStringBuilder = new SpannableStringBuilder(endDateLabel);
- } else {
- endDateStringBuilder = new SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(pinnedSslCertificateDateArray[1]));
- }
- }
-
- // Define the color spans.
- ForegroundColorSpan blueColorSpan;
- ForegroundColorSpan redColorSpan;
-
- // Get the current theme status.
- int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
-
- // Set the color spans according to the theme. The deprecated `getResources()` must be used until the minimum API >= 23.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
- blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.blue_700));
- redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700));
- } else {
- blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.violet_700));
- redColorSpan = new ForegroundColorSpan(getResources().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.getCurrentIpAddresses().equals(nestedScrollWebView.getPinnedIpAddresses())) {
- 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.equals(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.equals(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.equals(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.equals(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.equals(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.equals(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.equals(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.equals(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.setText(domainNameStringBuilder);
- ipAddressesTextView.setText(ipAddressesStringBuilder);
- issuedToCNameTextView.setText(issuedToCNameStringBuilder);
- issuedToONameTextView.setText(issuedToONameStringBuilder);
- issuedToUNameTextView.setText(issuedToUNameStringBuilder);
- issuedByCNameTextView.setText(issuedByCNameStringBuilder);
- issuedByONameTextView.setText(issuedByONameStringBuilder);
- issuedByUNameTextView.setText(issuedByUNameStringBuilder);
- startDateTextView.setText(startDateStringBuilder);
- endDateTextView.setText(endDateStringBuilder);
-
- // Display the tab.
- container.addView(tabViewGroup);
-
- // Make it so.
- return tabViewGroup;
- }
- }
-}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright © 2017-2021 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+ *
+ * Privacy Browser is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Privacy Browser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.stoutner.privacybrowser.dialogs
+
+import android.annotation.SuppressLint
+import android.app.Dialog
+import android.content.Context
+import android.content.DialogInterface
+import android.content.res.Configuration
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.Drawable
+import android.os.Bundle
+import android.view.WindowManager
+
+import androidx.appcompat.app.AlertDialog
+import androidx.core.content.ContextCompat
+import androidx.fragment.app.DialogFragment
+import androidx.preference.PreferenceManager
+import androidx.viewpager.widget.ViewPager
+
+import com.google.android.material.tabs.TabLayout
+
+import com.stoutner.privacybrowser.R
+import com.stoutner.privacybrowser.activities.MainWebViewActivity
+import com.stoutner.privacybrowser.adapters.PinnedMismatchPagerAdapter
+import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper
+import com.stoutner.privacybrowser.views.NestedScrollWebView
+
+// Declare the class constants.
+private const val WEBVIEW_FRAGMENT_ID = "webview_fragment_id"
+
+class PinnedMismatchDialog : DialogFragment() {
+ // Declare the class variables.
+ private lateinit var pinnedMismatchListener: PinnedMismatchListener
+
+ // The public interface is used to send information back to the parent activity.
+ interface PinnedMismatchListener {
+ fun pinnedErrorGoBack()
+ }
+
+ override fun onAttach(context: Context) {
+ // Run the default commands.
+ super.onAttach(context)
+
+ // Get a handle for the listener from the launching context.
+ pinnedMismatchListener = context as PinnedMismatchListener
+ }
+
+ 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
+ fun displayDialog(webViewFragmentId: Long): PinnedMismatchDialog {
+ // Create an arguments bundle.
+ val argumentsBundle = Bundle()
+
+ // Store the WebView fragment ID in the bundle.
+ argumentsBundle.putLong(WEBVIEW_FRAGMENT_ID, webViewFragmentId)
+
+ // Create a new instance of the pinned mismatch dialog.
+ val pinnedMismatchDialog = PinnedMismatchDialog()
+
+ // Add the arguments bundle to the new instance.
+ pinnedMismatchDialog.arguments = argumentsBundle
+
+ // Make it so.
+ return pinnedMismatchDialog
+ }
+ }
+
+ // `@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 WebView fragment ID.
+ val webViewFragmentId = requireArguments().getLong(WEBVIEW_FRAGMENT_ID)
+
+ // 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 fragment view.
+ val fragmentView = webViewTabFragment.requireView()
+
+ // Get a handle for the current WebView.
+ val nestedScrollWebView = fragmentView.findViewById<NestedScrollWebView>(R.id.nestedscroll_webview)!!
+
+ // Use an alert dialog builder to create the alert dialog.
+ val dialogBuilder = AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog)
+
+ // Get the favorite icon.
+ val favoriteIconBitmap = nestedScrollWebView.favoriteOrDefaultIcon
+
+ // Get the default favorite icon drawable. `ContextCompat` must be used until API >= 21.
+ val defaultFavoriteIconDrawable = ContextCompat.getDrawable(requireContext(), R.drawable.world)
+
+ // Cast the favorite icon drawable to a bitmap drawable.
+ val defaultFavoriteIconBitmapDrawable = (defaultFavoriteIconDrawable as BitmapDrawable)
+
+ // Store the default icon bitmap.
+ val defaultFavoriteIconBitmap = defaultFavoriteIconBitmapDrawable.bitmap
+
+ // Set the favorite icon as the dialog icon if it exists.
+ if (favoriteIconBitmap.sameAs(defaultFavoriteIconBitmap)) { // There is no website favorite icon.
+ // 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.ssl_certificate_enabled_day)
+ } else {
+ dialogBuilder.setIcon(R.drawable.ssl_certificate_enabled_night)
+ }
+ } else { // There is a favorite icon.
+ // Create a drawable version of the favorite icon.
+ val favoriteIconDrawable: Drawable = BitmapDrawable(resources, favoriteIconBitmap)
+
+ // Set the icon.
+ dialogBuilder.setIcon(favoriteIconDrawable)
+ }
+
+ // Set the title.
+ dialogBuilder.setTitle(R.string.pinned_mismatch)
+
+ // Set the layout. The parent view is `null` because it will be assigned by the alert dialog.
+ dialogBuilder.setView(requireActivity().layoutInflater.inflate(R.layout.pinned_mismatch_linearlayout, null))
+
+ // Set the update button listener.
+ dialogBuilder.setNeutralButton(R.string.update) { _: DialogInterface?, _: Int ->
+ // Get the current SSL certificate.
+ val currentSslCertificate = nestedScrollWebView.certificate!!
+
+ // Get the dates from the certificate.
+ val currentSslStartDate = currentSslCertificate.validNotBeforeDate
+ val currentSslEndDate = currentSslCertificate.validNotAfterDate
+
+ // Convert the dates into longs. If the date is null, a long value of `0` will be stored in the domains database entry.
+ val currentSslStartDateLong: Long = currentSslStartDate?.time ?: 0
+ val currentSslEndDateLong: Long = currentSslEndDate?.time ?: 0
+
+ // Initialize the database handler. The `0` specifies the database version, but that is ignored and set instead using a constant in the domains database helper.
+ val domainsDatabaseHelper = DomainsDatabaseHelper(context, null, null, 0)
+
+ // Update the SSL certificate if it is pinned.
+ if (nestedScrollWebView.hasPinnedSslCertificate()) {
+ // Update the pinned SSL certificate in the domain database.
+ domainsDatabaseHelper.updatePinnedSslCertificate(nestedScrollWebView.domainSettingsDatabaseId, currentSslCertificate.issuedTo.cName, currentSslCertificate.issuedTo.oName,
+ currentSslCertificate.issuedTo.uName, currentSslCertificate.issuedBy.cName, currentSslCertificate.issuedBy.oName, currentSslCertificate.issuedBy.uName, currentSslStartDateLong,
+ currentSslEndDateLong)
+
+ // Update the pinned SSL certificate in the nested scroll WebView.
+ nestedScrollWebView.setPinnedSslCertificate(currentSslCertificate.issuedTo.cName, currentSslCertificate.issuedTo.oName, currentSslCertificate.issuedTo.uName,
+ currentSslCertificate.issuedBy.cName, currentSslCertificate.issuedBy.oName, currentSslCertificate.issuedBy.uName, currentSslStartDate, currentSslEndDate)
+ }
+
+ // Update the IP addresses if they are pinned.
+ if (nestedScrollWebView.hasPinnedIpAddresses()) {
+ // Update the pinned IP addresses in the domain database.
+ domainsDatabaseHelper.updatePinnedIpAddresses(nestedScrollWebView.domainSettingsDatabaseId, nestedScrollWebView. currentIpAddresses)
+
+ // Update the pinned IP addresses in the nested scroll WebView.
+ nestedScrollWebView.pinnedIpAddresses = nestedScrollWebView.currentIpAddresses
+ }
+ }
+
+ // Set the back button listener.
+ dialogBuilder.setNegativeButton(R.string.back) { _: DialogInterface?, _: Int ->
+ if (nestedScrollWebView.canGoBack()) { // There is a back page in the history.
+ // Invoke the navigate history listener in the calling activity. These commands cannot be run here because they need access to `applyDomainSettings()`.
+ pinnedMismatchListener.pinnedErrorGoBack()
+ } else { // There are no pages to go back to.
+ // Load a blank page
+ nestedScrollWebView.loadUrl("")
+ }
+ }
+
+ // Set the proceed button listener.
+ dialogBuilder.setPositiveButton(R.string.proceed) { _: DialogInterface?, _: Int ->
+ // Do not check the pinned information for this domain again until the domain changes.
+ nestedScrollWebView.setIgnorePinnedDomainInformation(true)
+ }
+
+ // 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 handles for the views.
+ val viewPager = alertDialog.findViewById<ViewPager>(R.id.pinned_ssl_certificate_mismatch_viewpager)!!
+ val tabLayout = alertDialog.findViewById<TabLayout>(R.id.pinned_ssl_certificate_mismatch_tablayout)!!
+
+ // Initialize the pinned mismatch pager adapter.
+ val pinnedMismatchPagerAdapter = PinnedMismatchPagerAdapter(requireContext(), layoutInflater, webViewFragmentId)
+
+ // Set the view pager adapter.
+ viewPager.adapter = pinnedMismatchPagerAdapter
+
+ // Connect the tab layout to the view pager.
+ tabLayout.setupWithViewPager(viewPager)
+
+ // Return the alert dialog.
+ return alertDialog
+ }
+}
\ No newline at end of file
+++ /dev/null
-/*
- * Copyright © 2019-2020 Soren Stoutner <soren@stoutner.com>.
- *
- * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
- *
- * Privacy Browser is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Privacy Browser is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.stoutner.privacybrowser.dialogs;
-
-import android.app.Dialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.SharedPreferences;
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.view.WindowManager;
-
-import androidx.annotation.NonNull;
-import androidx.appcompat.app.AlertDialog;
-import androidx.fragment.app.DialogFragment;
-import androidx.preference.PreferenceManager;
-
-
-import com.stoutner.privacybrowser.helpers.ProxyHelper;
-import com.stoutner.privacybrowser.R;
-
-public class ProxyNotInstalledDialog extends DialogFragment {
- public static ProxyNotInstalledDialog displayDialog(String proxyMode) {
- // Create an arguments bundle.
- Bundle argumentsBundle = new Bundle();
-
- // Store the proxy mode in the bundle.
- argumentsBundle.putString("proxy_mode", proxyMode);
-
- // Create a new instance of the dialog.
- ProxyNotInstalledDialog proxyNotInstalledDialog = new ProxyNotInstalledDialog();
-
- // Add the bundle to the dialog.
- proxyNotInstalledDialog.setArguments(argumentsBundle);
-
- // Return the new dialog.
- return proxyNotInstalledDialog;
- }
-
- @Override
- @NonNull
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- // Get the context.
- Context context = requireContext();
-
- // Get the arguments.
- Bundle arguments = getArguments();
-
- // Remove the incorrect lint warning below that the arguments might be null.
- assert arguments != null;
-
- // Get the proxy mode from the arguments.
- String proxyMode = arguments.getString("proxy_mode");
-
- // Remove the incorrect lint warning below that the proxy mode might be null.
- assert proxyMode != null;
-
- // Use a 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 and the message according to the proxy mode.
- switch (proxyMode) {
- case ProxyHelper.TOR:
- // Set the title.
- dialogBuilder.setTitle(R.string.orbot_not_installed_title);
-
- // Set the message.
- dialogBuilder.setMessage(R.string.orbot_not_installed_message);
- break;
-
- case ProxyHelper.I2P:
- // Set the title.
- dialogBuilder.setTitle(R.string.i2p_not_installed_title);
-
- // Set the message.
- dialogBuilder.setMessage(R.string.i2p_not_installed_message);
- break;
- }
-
- // Set the positive button.
- dialogBuilder.setPositiveButton(R.string.close, (DialogInterface dialog, int which) -> {
- // Do nothing. The alert dialog will close automatically.
- });
-
- // Create an alert dialog from the alert dialog builder.
- AlertDialog alertDialog = dialogBuilder.create();
-
- // 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) {
- // Remove the warning below that `getWindows()` might be null.
- assert alertDialog.getWindow() != null;
-
- // Disable screenshots.
- alertDialog.getWindow().addFlags((WindowManager.LayoutParams.FLAG_SECURE));
- }
-
- // Return the alert dialog.
- return alertDialog;
- }
-}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright © 2019-2021 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+ *
+ * Privacy Browser is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Privacy Browser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.stoutner.privacybrowser.dialogs
+
+import android.app.Dialog
+import android.content.res.Configuration
+import android.os.Bundle
+import android.view.WindowManager
+
+import androidx.appcompat.app.AlertDialog
+import androidx.fragment.app.DialogFragment
+import androidx.preference.PreferenceManager
+
+import com.stoutner.privacybrowser.R
+import com.stoutner.privacybrowser.helpers.ProxyHelper
+
+// Declare the class constants.
+private const val PROXY_MODE = "proxy_mode"
+
+class ProxyNotInstalledDialog : DialogFragment() {
+ companion object {
+ // `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin.
+ @JvmStatic
+ fun displayDialog(proxyMode: String): ProxyNotInstalledDialog {
+ // Create an arguments bundle.
+ val argumentsBundle = Bundle()
+
+ // Store the proxy mode in the bundle.
+ argumentsBundle.putString(PROXY_MODE, proxyMode)
+
+ // Create a new instance of the dialog.
+ val proxyNotInstalledDialog = ProxyNotInstalledDialog()
+
+ // Add the bundle to the dialog.
+ proxyNotInstalledDialog.arguments = argumentsBundle
+
+ // Return the new dialog.
+ return proxyNotInstalledDialog
+ }
+ }
+
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ // Get the proxy mode from the arguments.
+ val proxyMode = requireArguments().getString(PROXY_MODE)!!
+
+ // Use a 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 and the message according to the proxy mode.
+ when (proxyMode) {
+ ProxyHelper.TOR -> {
+ // Set the title.
+ dialogBuilder.setTitle(R.string.orbot_not_installed_title)
+
+ // Set the message.
+ dialogBuilder.setMessage(R.string.orbot_not_installed_message)
+ }
+
+ ProxyHelper.I2P -> {
+ // Set the title.
+ dialogBuilder.setTitle(R.string.i2p_not_installed_title)
+
+ // Set the message.
+ dialogBuilder.setMessage(R.string.i2p_not_installed_message)
+ }
+ }
+
+ // Set the close button listener. Using `null` as the listener closes the dialog without doing anything else.
+ dialogBuilder.setPositiveButton(R.string.close, null)
+
+ // 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) {
+ alertDialog.window!!.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
+ }
+
+ // Return the alert dialog.
+ return alertDialog
+ }
+}
\ No newline at end of file
+++ /dev/null
-/*
- * Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
- *
- * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
- *
- * Privacy Browser is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Privacy Browser is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.stoutner.privacybrowser.dialogs;
-
-import android.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.preference.PreferenceManager;
-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 com.stoutner.privacybrowser.R;
-import com.stoutner.privacybrowser.helpers.DownloadLocationHelper;
-
-import java.io.File;
-
-public class SaveDialog extends DialogFragment {
- // Declare the save listener.
- private SaveListener saveListener;
-
- // The public interface is used to send information back to the parent activity.
- public interface SaveListener {
- void onSave(int saveType, DialogFragment dialogFragment);
- }
-
- // Declare the class constants.
- public static final int SAVE_LOGCAT = 0;
- public static final int SAVE_ABOUT_VERSION_TEXT = 1;
- public static final int SAVE_ABOUT_VERSION_IMAGE = 2;
- private static final String SAVE_TYPE = "save_type";
-
- // Declare the class variables.
- String fileName;
-
- @Override
- public void onAttach(@NonNull Context context) {
- // Run the default commands.
- super.onAttach(context);
-
- // Get a handle for save listener from the launching context.
- saveListener = (SaveListener) context;
- }
-
- public static SaveDialog save(int saveType) {
- // Create an arguments bundle.
- Bundle argumentsBundle = new Bundle();
-
- // Store the arguments in the bundle.
- argumentsBundle.putInt(SAVE_TYPE, saveType);
-
- // Create a new instance of the save dialog.
- SaveDialog saveDialog = new SaveDialog();
-
- // Add the arguments bundle to the dialog.
- saveDialog.setArguments(argumentsBundle);
-
- // Return the new dialog.
- return saveDialog;
- }
-
- // `@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 arguments.
- Bundle arguments = getArguments();
-
- // Remove the incorrect lint warning that the arguments might be null.
- assert arguments != null;
-
- // Get the arguments from the bundle.
- int saveType = arguments.getInt(SAVE_TYPE);
-
- // 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 title and icon according to the type.
- switch (saveType) {
- case SAVE_LOGCAT:
- // Set the title.
- dialogBuilder.setTitle(R.string.save_logcat);
-
- // Set the icon according to the theme.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
- dialogBuilder.setIcon(R.drawable.save_dialog_day);
- } else {
- dialogBuilder.setIcon(R.drawable.save_dialog_night);
- }
- break;
-
- case SAVE_ABOUT_VERSION_TEXT:
- // Set the title.
- dialogBuilder.setTitle(R.string.save_text);
-
- // Set the icon according to the theme.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
- dialogBuilder.setIcon(R.drawable.save_text_blue_day);
- } else {
- dialogBuilder.setIcon(R.drawable.save_text_blue_night);
- }
- break;
-
- case SAVE_ABOUT_VERSION_IMAGE:
- // Set the title.
- dialogBuilder.setTitle(R.string.save_image);
-
- // Set the icon according to the theme.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
- dialogBuilder.setIcon(R.drawable.images_enabled_day);
- } else {
- dialogBuilder.setIcon(R.drawable.images_enabled_night);
- }
- break;
- }
-
- // Set the view. The parent view is null because it will be assigned by the alert dialog.
- dialogBuilder.setView(activity.getLayoutInflater().inflate(R.layout.save_dialog, null));
-
- // 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 save button listener.
- dialogBuilder.setPositiveButton(R.string.save, (DialogInterface dialog, int which) -> {
- // Return the dialog fragment to the parent activity.
- saveListener.onSave(saveType, this);
- });
-
- // Create an alert dialog from the builder.
- AlertDialog alertDialog = dialogBuilder.create();
-
- // Remove the incorrect lint warning below that `getWindow()` might be null.
- assert alertDialog.getWindow() != null;
-
- // Get a handle for the shared preferences.
- SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());
-
- // Get the screenshot preference.
- boolean allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), 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 fileExistsWarningTextView = alertDialog.findViewById(R.id.file_exists_warning_textview);
- TextView storagePermissionTextView = alertDialog.findViewById(R.id.storage_permission_textview);
- Button saveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
-
- // Remove the incorrect lint warnings below that the views might be null.
- assert fileNameEditText != null;
- assert browseButton != null;
- assert fileExistsWarningTextView != null;
- assert storagePermissionTextView != null;
-
- // Update the status of the save button when the file name changes.
- fileNameEditText.addTextChangedListener(new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- // Do nothing.
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- // Do nothing.
- }
-
- @Override
- public void afterTextChanged(Editable s) {
- // 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()) {
- // Show the file exists warning.
- fileExistsWarningTextView.setVisibility(View.VISIBLE);
- } else {
- // Hide the file exists warning.
- fileExistsWarningTextView.setVisibility(View.GONE);
- }
-
- // Enable the save button if the file name is populated.
- saveButton.setEnabled(!fileNameString.isEmpty());
- }
- });
-
- // Set the file name according to the type.
- switch (saveType) {
- case SAVE_LOGCAT:
- // Use a file name ending in `.txt`.
- fileName = getString(R.string.privacy_browser_logcat_txt);
- break;
-
- case SAVE_ABOUT_VERSION_TEXT:
- // Use a file name ending in `.txt`.
- fileName = getString(R.string.privacy_browser_version_txt);
- break;
-
- case SAVE_ABOUT_VERSION_IMAGE:
- // Use a file name ending in `.png`.
- fileName = getString(R.string.privacy_browser_version_png);
- break;
- }
-
- // Instantiate the download location helper.
- DownloadLocationHelper downloadLocationHelper = new DownloadLocationHelper();
-
- // Get the default file path.
- String defaultFilePath = downloadLocationHelper.getDownloadLocation(context) + "/" + fileName;
-
- // Display the default file path.
- fileNameEditText.setText(defaultFilePath);
-
- // 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_CREATE_DOCUMENT);
-
- // Set the intent MIME type to include all files so that everything is visible.
- browseIntent.setType("*/*");
-
- // Set the initial file name.
- browseIntent.putExtra(Intent.EXTRA_TITLE, fileName);
-
- // Set the initial directory if the minimum API >= 26.
- if (Build.VERSION.SDK_INT >= 26) {
- browseIntent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, Environment.getExternalStorageDirectory());
- }
-
- // Request a file that can be opened.
- browseIntent.addCategory(Intent.CATEGORY_OPENABLE);
-
- // Launch the file picker. There is only one `startActivityForResult()`, so the request code is simply set to 0, but it must be run under `activity` so the request code is correct.
- activity.startActivityForResult(browseIntent, 0);
- });
-
- // Return the alert dialog.
- return alertDialog;
- }
-}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright © 2016-2021 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+ *
+ * Privacy Browser is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Privacy Browser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.stoutner.privacybrowser.dialogs
+
+import android.Manifest
+import android.annotation.SuppressLint
+import android.os.Bundle
+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.view.View
+import android.view.WindowManager
+import android.widget.Button
+import android.widget.EditText
+import android.widget.TextView
+import android.text.TextWatcher
+import android.text.Editable
+
+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.helpers.DownloadLocationHelper
+
+import java.io.File
+
+// Declare the class constants.
+private const val SAVE_TYPE = "save_type"
+
+class SaveDialog : DialogFragment() {
+ // Declare the class variables.
+ private lateinit var saveListener: SaveListener
+ private lateinit var fileName: String
+
+ // The public interface is used to send information back to the parent activity.
+ interface SaveListener {
+ fun onSave(saveType: Int, dialogFragment: DialogFragment)
+ }
+
+ override fun onAttach(context: Context) {
+ // Run the default commands.
+ super.onAttach(context)
+
+ // Get a handle for the save listener from the launching context.
+ saveListener = context as SaveListener
+ }
+
+ companion object {
+ // Declare the companion object constants. These can be moved to class constants once all of the code has transitioned to Kotlin.
+ const val SAVE_LOGCAT = 0
+ const val SAVE_ABOUT_VERSION_TEXT = 1
+ const val SAVE_ABOUT_VERSION_IMAGE = 2
+
+ // `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin.
+ @JvmStatic
+ fun save(saveType: Int): SaveDialog {
+ // Create an arguments bundle.
+ val argumentsBundle = Bundle()
+
+ // Store the arguments in the bundle.
+ argumentsBundle.putInt(SAVE_TYPE, saveType)
+
+ // Create a new instance of the save dialog.
+ val saveDialog = SaveDialog()
+
+ // Add the arguments bundle to the dialog.
+ saveDialog.arguments = argumentsBundle
+
+ // Return the new dialog.
+ return saveDialog
+ }
+ }
+
+ // `@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 arguments from the bundle.
+ val saveType = requireArguments().getInt(SAVE_TYPE)
+
+ // 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 title and the icon according to the save type.
+ when (saveType) {
+ SAVE_LOGCAT -> {
+ // Set the title.
+ dialogBuilder.setTitle(R.string.save_logcat)
+
+ // Set the icon according to the theme.
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+ dialogBuilder.setIcon(R.drawable.save_dialog_day)
+ } else {
+ dialogBuilder.setIcon(R.drawable.save_dialog_night)
+ }
+ }
+
+ SAVE_ABOUT_VERSION_TEXT -> {
+ // Set the title.
+ dialogBuilder.setTitle(R.string.save_text)
+
+ // Set the icon according to the theme.
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+ dialogBuilder.setIcon(R.drawable.save_text_blue_day)
+ } else {
+ dialogBuilder.setIcon(R.drawable.save_text_blue_night)
+ }
+ }
+
+ SAVE_ABOUT_VERSION_IMAGE -> {
+ // Set the title.
+ dialogBuilder.setTitle(R.string.save_image)
+
+ // Set the icon according to the theme.
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+ dialogBuilder.setIcon(R.drawable.images_enabled_day)
+ } else {
+ dialogBuilder.setIcon(R.drawable.images_enabled_night)
+ }
+ }
+ }
+
+ // Set the view. The parent view is null because it will be assigned by the alert dialog.
+ dialogBuilder.setView(requireActivity().layoutInflater.inflate(R.layout.save_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 save button listener.
+ dialogBuilder.setPositiveButton(R.string.save) { _: DialogInterface?, _: Int ->
+ // Return the dialog fragment to the parent activity.
+ saveListener.onSave(saveType, 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<EditText>(R.id.file_name_edittext)!!
+ val browseButton = alertDialog.findViewById<Button>(R.id.browse_button)!!
+ val fileExistsWarningTextView = alertDialog.findViewById<TextView>(R.id.file_exists_warning_textview)!!
+ val storagePermissionTextView = alertDialog.findViewById<TextView>(R.id.storage_permission_textview)!!
+ val saveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE)
+
+ // Update the status of the save button when the file name changes.
+ fileNameEditText.addTextChangedListener(object : TextWatcher {
+ override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
+ // Do nothing.
+ }
+
+ override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
+ // Do nothing.
+ }
+
+ override fun afterTextChanged(s: Editable) {
+ // Get the current file name.
+ val fileNameString = fileNameEditText.text.toString()
+
+ // Convert the file name string to a file.
+ val file = File(fileNameString)
+
+ // Check to see if the file exists.
+ if (file.exists()) {
+ // Show the file exists warning.
+ fileExistsWarningTextView.visibility = View.VISIBLE
+ } else {
+ // Hide the file exists warning.
+ fileExistsWarningTextView.visibility = View.GONE
+ }
+
+ // Enable the save button if the file name is populated.
+ saveButton.isEnabled = fileNameString.isNotEmpty()
+ }
+ })
+
+ // Set the file name according to the type.
+ when (saveType) {
+ SAVE_LOGCAT -> fileName = getString(R.string.privacy_browser_logcat_txt)
+ SAVE_ABOUT_VERSION_TEXT -> fileName = getString(R.string.privacy_browser_version_txt)
+ SAVE_ABOUT_VERSION_IMAGE -> fileName = getString(R.string.privacy_browser_version_png)
+ }
+
+ // Instantiate the download location helper.
+ val downloadLocationHelper = DownloadLocationHelper()
+
+ // Get the default file path.
+ val defaultFilePath = downloadLocationHelper.getDownloadLocation(context) + "/" + fileName
+
+ // Display the default file path.
+ fileNameEditText.setText(defaultFilePath)
+
+ // Hide the storage permission text view if the permission has already been granted.
+ if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
+ storagePermissionTextView.visibility = View.GONE
+ }
+
+ // Handle clicks on the browse button.
+ browseButton.setOnClickListener {
+ // Create the file picker intent.
+ val browseIntent = Intent(Intent.ACTION_CREATE_DOCUMENT)
+
+ // Set the intent MIME type to include all files so that everything is visible.
+ browseIntent.type = "*/*"
+
+ // Set the initial file name.
+ browseIntent.putExtra(Intent.EXTRA_TITLE, fileName)
+
+ // Request a file that can be opened.
+ browseIntent.addCategory(Intent.CATEGORY_OPENABLE)
+
+ // Launch the file picker. There is only one `startActivityForResult()`, so the request code is simply set to 0, but it must be run under `activity` so the request code is correct.
+ requireActivity().startActivityForResult(browseIntent, 0)
+ }
+
+ // Return the alert dialog.
+ return alertDialog
+ }
+}
\ No newline at end of file
+++ /dev/null
-/*
- * Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
- *
- * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
- *
- * Privacy Browser is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Privacy Browser is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.stoutner.privacybrowser.fragments;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.net.Uri;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.webkit.WebResourceResponse;
-import android.webkit.WebView;
-import android.webkit.WebViewClient;
-
-import androidx.annotation.NonNull;
-import androidx.fragment.app.Fragment;
-import androidx.webkit.WebSettingsCompat;
-import androidx.webkit.WebViewAssetLoader;
-import androidx.webkit.WebViewFeature;
-
-import com.stoutner.privacybrowser.R;
-
-public class AboutWebViewFragment extends Fragment {
- // Declare the class constants.
- final static String TAB_NUMBER = "tab_number";
-
- // Declare the class variables.
- private int tabNumber;
-
- // Declare the class views.
- private View webViewLayout;
-
- public static AboutWebViewFragment createTab(int tabNumber) {
- // Create an arguments bundle.
- Bundle argumentsBundle = new Bundle();
-
- // Store the arguments in the bundle.
- argumentsBundle.putInt(TAB_NUMBER, tabNumber);
-
- // Create a new instance of the tab fragment.
- AboutWebViewFragment aboutWebViewFragment = new AboutWebViewFragment();
-
- // Add the arguments bundle to the fragment.
- aboutWebViewFragment.setArguments(argumentsBundle);
-
- // Return the new fragment.
- return aboutWebViewFragment;
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- // Run the default commands.
- super.onCreate(savedInstanceState);
-
- // Get a handle for the arguments.
- Bundle arguments = getArguments();
-
- // Remove the incorrect lint warning below that arguments might be null.
- assert arguments != null;
-
- // Store the tab number in a class variable.
- tabNumber = arguments.getInt(TAB_NUMBER);
- }
-
- @Override
- public View onCreateView(@NonNull LayoutInflater layoutInflater, ViewGroup container, Bundle savedInstanceState) {
- // Inflate the layout. Setting false at the end of inflater.inflate does not attach the inflated layout as a child of container. The fragment will take care of attaching the root automatically.
- webViewLayout = layoutInflater.inflate(R.layout.bare_webview, container, false);
-
- // Get a handle for tab WebView.
- WebView tabWebView = (WebView) webViewLayout;
-
- // Get a handle for the context.
- Context context = getContext();
-
- // Remove the incorrect lint warning below that the context might be null.
- assert context != null;
-
- // Create a WebView asset loader.
- final WebViewAssetLoader webViewAssetLoader = new WebViewAssetLoader.Builder().addPathHandler("/assets/", new WebViewAssetLoader.AssetsPathHandler(context)).build();
-
- // Set a WebView client.
- tabWebView.setWebViewClient(new WebViewClient() {
- // `shouldOverrideUrlLoading` allows the sending of external links back to the main Privacy Browser WebView. The deprecated `shouldOverrideUrlLoading` must be used until API >= 24.
- @Override
- public boolean shouldOverrideUrlLoading(WebView view, String url) {
- // Create an intent to view the URL.
- Intent urlIntent = new Intent(Intent.ACTION_VIEW);
-
- // Add the URL to the intent.
- urlIntent.setData(Uri.parse(url));
-
- // Make it so.
- startActivity(urlIntent);
- return true;
- }
-
- @Override
- public WebResourceResponse shouldInterceptRequest(WebView webView, String url) {
- // Have the WebView asset loader process the request. This allows the loading of SVG files, which otherwise is prevented by the CORS policy.
- return webViewAssetLoader.shouldInterceptRequest(Uri.parse(url));
- }
- });
-
- // Get the current theme status.
- int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
-
- // Check to see if the app is in night mode.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES && WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) { // The app is in night mode.
- // Apply the dark WebView theme.
- WebSettingsCompat.setForceDark(tabWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
- }
-
- // Load the indicated tab. The tab numbers start at 0, with the WebView tabs starting at 1.
- switch (tabNumber) {
- case 1:
- // Load the Permissions tab.
- tabWebView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.android_asset_path) + "/about_permissions.html");
- break;
-
- case 2:
- // Load the Privacy Policy tab.
- tabWebView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.android_asset_path) + "/about_privacy_policy.html");
- break;
-
- case 3:
- // Load the Changelog tab.
- tabWebView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.android_asset_path) + "/about_changelog.html");
- break;
-
- case 4:
- // Load the Licenses tab.
- tabWebView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.android_asset_path) + "/about_licenses.html");
- break;
-
- case 5:
- // Load the Contributors tab.
- tabWebView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.android_asset_path) + "/about_contributors.html");
- break;
-
- case 6:
- // Load the Links tab.
- tabWebView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.android_asset_path) + "/about_links.html");
- break;
- }
-
- // Scroll the tab if the saved instance state is not null.
- if (savedInstanceState != null) {
- tabWebView.post(() -> {
- tabWebView.setScrollX(savedInstanceState.getInt("scroll_x"));
- tabWebView.setScrollY(savedInstanceState.getInt("scroll_y"));
- });
- }
-
- // Return the formatted WebView layout.
- return webViewLayout;
- }
-
- @Override
- public void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
- // Run the default commands.
- super.onSaveInstanceState(savedInstanceState);
-
-
- // Get a handle for the tab WebView. A class variable cannot be used because it gets out of sync when restarting.
- WebView tabWebView = (WebView) webViewLayout;
-
- // Save the scroll positions if the layout is not null, which can happen if a tab is not currently selected.
- if (tabWebView != null) {
- savedInstanceState.putInt("scroll_x", tabWebView.getScrollX());
- savedInstanceState.putInt("scroll_y", tabWebView.getScrollY());
- }
- }
-}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright © 2016-2021 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+ *
+ * Privacy Browser is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Privacy Browser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.stoutner.privacybrowser.fragments
+
+import android.content.Intent
+import android.content.res.Configuration
+import android.net.Uri
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.webkit.WebResourceResponse
+import android.webkit.WebView
+import android.webkit.WebViewClient
+
+import androidx.fragment.app.Fragment
+import androidx.webkit.WebSettingsCompat
+import androidx.webkit.WebViewAssetLoader
+import androidx.webkit.WebViewAssetLoader.AssetsPathHandler
+import androidx.webkit.WebViewFeature
+
+import com.stoutner.privacybrowser.R
+
+// Declare the class constants.
+private const val TAB_NUMBER = "tab_number"
+
+class AboutWebViewFragment : Fragment() {
+ // Declare the class variables.
+ private var tabNumber = 0
+
+ // Declare the class views.
+ private lateinit var webViewLayout: View
+
+ companion object {
+ fun createTab(tabNumber: Int): AboutWebViewFragment {
+ // Create an arguments bundle.
+ val argumentsBundle = Bundle()
+
+ // Store the arguments in the bundle.
+ argumentsBundle.putInt(TAB_NUMBER, tabNumber)
+
+ // Create a new instance of the tab fragment.
+ val aboutWebViewFragment = AboutWebViewFragment()
+
+ // Add the arguments bundle to the fragment.
+ aboutWebViewFragment.arguments = argumentsBundle
+
+ // Return the new fragment.
+ return aboutWebViewFragment
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ // Run the default commands.
+ super.onCreate(savedInstanceState)
+
+ // Store the tab number in a class variable.
+ tabNumber = requireArguments().getInt(TAB_NUMBER)
+ }
+
+ override fun onCreateView(layoutInflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+ // Inflate the layout. False does not attach the inflated layout as a child of container. The fragment will take care of attaching the root automatically.
+ webViewLayout = layoutInflater.inflate(R.layout.bare_webview, container, false)
+
+ // Get a handle for tab WebView.
+ val tabWebView = webViewLayout as WebView
+
+ // Create a WebView asset loader.
+ val webViewAssetLoader = WebViewAssetLoader.Builder().addPathHandler("/assets/", AssetsPathHandler(requireContext())).build()
+
+ // Set a WebView client.
+ tabWebView.webViewClient = object : WebViewClient() {
+ // `shouldOverrideUrlLoading` allows the sending of external links back to the main Privacy Browser WebView. The deprecated `shouldOverrideUrlLoading` must be used until API >= 24.
+ override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
+ // Create an intent to view the URL.
+ val urlIntent = Intent(Intent.ACTION_VIEW)
+
+ // Add the URL to the intent.
+ urlIntent.data = Uri.parse(url)
+
+ // Make it so.
+ startActivity(urlIntent)
+ return true
+ }
+
+ override fun shouldInterceptRequest(webView: WebView, url: String): WebResourceResponse? {
+ // Have the WebView asset loader process the request. This allows the loading of SVG files, which otherwise is prevented by the CORS policy.
+ return webViewAssetLoader.shouldInterceptRequest(Uri.parse(url))
+ }
+ }
+
+ // Get the current theme status.
+ val currentThemeStatus = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
+
+ // Check to see if the app is in night mode.
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES && WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) { // The app is in night mode.
+ // Apply the dark WebView theme.
+ WebSettingsCompat.setForceDark(tabWebView.settings, WebSettingsCompat.FORCE_DARK_ON)
+ }
+
+ // Load the indicated tab. The tab numbers start at 0, with the WebView tabs starting at 1.
+ when (tabNumber) {
+ 1 -> tabWebView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.android_asset_path) + "/about_permissions.html")
+ 2 -> tabWebView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.android_asset_path) + "/about_privacy_policy.html")
+ 3 -> tabWebView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.android_asset_path) + "/about_changelog.html")
+ 4 -> tabWebView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.android_asset_path) + "/about_licenses.html")
+ 5 -> tabWebView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.android_asset_path) + "/about_contributors.html")
+ 6 -> tabWebView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.android_asset_path) + "/about_links.html")
+ }
+
+ // Scroll the tab if the saved instance state is not null.
+ if (savedInstanceState != null) {
+ tabWebView.post {
+ tabWebView.scrollX = savedInstanceState.getInt("scroll_x")
+ tabWebView.scrollY = savedInstanceState.getInt("scroll_y")
+ }
+ }
+
+ // Return the formatted WebView layout.
+ return webViewLayout
+ }
+
+ override fun onSaveInstanceState(savedInstanceState: Bundle) {
+ // Run the default commands.
+ super.onSaveInstanceState(savedInstanceState)
+
+ // Get a handle for the tab WebView. A class variable cannot be used because it gets out of sync when restarting.
+ val tabWebView = webViewLayout as WebView?
+
+ // Save the scroll positions if the layout is not null, which can happen if a tab is not currently selected.
+ if (tabWebView != null) {
+ savedInstanceState.putInt("scroll_x", tabWebView.scrollX)
+ savedInstanceState.putInt("scroll_y", tabWebView.scrollY)
+ }
+ }
+}
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- Copyright © 2017-2020 Soren Stoutner <soren@stoutner.com>.
-
- This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
-
- Privacy Browser is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- Privacy Browser is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>. -->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:padding="10dp"
- android:orientation="vertical" >
-
- <!-- Domain. -->
- <TextView
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:layout_marginTop="5dp"
- android:text="@string/domain"
- android:textAllCaps="true"
- android:textStyle="bold"
- android:textColor="?attr/blueTitleTextColor" />
-
- <TextView
- android:id="@+id/domain_name"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content" />
-
- <TextView
- android:id="@+id/ip_addresses"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"/>
-
-
- <!-- Issued To. -->
- <TextView
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:layout_marginTop="15dp"
- android:text="@string/issued_to"
- android:textAllCaps="true"
- android:textStyle="bold"
- android:textColor="?attr/blueTitleTextColor" />
-
- <TextView
- android:id="@+id/issued_to_cname"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content" />
-
- <TextView
- android:id="@+id/issued_to_oname"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content" />
-
- <TextView
- android:id="@+id/issued_to_uname"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content" />
-
-
- <!-- Issued By. -->
- <TextView
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:layout_marginTop="15dp"
- android:text="@string/issued_by"
- android:textAllCaps="true"
- android:textStyle="bold"
- android:textColor="?attr/blueTitleTextColor" />
-
- <TextView
- android:id="@+id/issued_by_cname"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content" />
-
- <TextView
- android:id="@+id/issued_by_oname"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content" />
-
- <TextView
- android:id="@+id/issued_by_uname"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content" />
-
-
- <!-- Valid Dates. -->
- <TextView
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:layout_marginTop="15dp"
- android:text="@string/valid_dates"
- android:textAllCaps="true"
- android:textStyle="bold"
- android:textColor="?attr/blueTitleTextColor" />
-
- <TextView
- android:id="@+id/start_date"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content" />
-
- <TextView
- android:id="@+id/end_date"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content" />
-</LinearLayout>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ Copyright © 2017-2021 Soren Stoutner <soren@stoutner.com>.
+
+ This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+
+ Privacy Browser is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Privacy Browser is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>. -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:padding="10dp"
+ android:orientation="vertical" >
+
+ <!-- Domain. -->
+ <TextView
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_marginTop="5dp"
+ android:text="@string/domain"
+ android:textAllCaps="true"
+ android:textStyle="bold"
+ android:textColor="?attr/blueTitleTextColor" />
+
+ <TextView
+ android:id="@+id/domain_name"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content" />
+
+ <TextView
+ android:id="@+id/ip_addresses"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"/>
+
+
+ <!-- Issued To. -->
+ <TextView
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_marginTop="15dp"
+ android:text="@string/issued_to"
+ android:textAllCaps="true"
+ android:textStyle="bold"
+ android:textColor="?attr/blueTitleTextColor" />
+
+ <TextView
+ android:id="@+id/issued_to_cname"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content" />
+
+ <TextView
+ android:id="@+id/issued_to_oname"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content" />
+
+ <TextView
+ android:id="@+id/issued_to_uname"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content" />
+
+
+ <!-- Issued By. -->
+ <TextView
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_marginTop="15dp"
+ android:text="@string/issued_by"
+ android:textAllCaps="true"
+ android:textStyle="bold"
+ android:textColor="?attr/blueTitleTextColor" />
+
+ <TextView
+ android:id="@+id/issued_by_cname"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content" />
+
+ <TextView
+ android:id="@+id/issued_by_oname"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content" />
+
+ <TextView
+ android:id="@+id/issued_by_uname"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content" />
+
+
+ <!-- Valid Dates. -->
+ <TextView
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_marginTop="15dp"
+ android:text="@string/valid_dates"
+ android:textAllCaps="true"
+ android:textStyle="bold"
+ android:textColor="?attr/blueTitleTextColor" />
+
+ <TextView
+ android:id="@+id/start_date"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content" />
+
+ <TextView
+ android:id="@+id/end_date"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content" />
+</LinearLayout>
\ No newline at end of file
/*
- * Copyright © 2016-2018 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2016-2018,2020 Soren Stoutner <soren@stoutner.com>.
*
* This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
*
*/
package com.stoutner.privacybrowser.helpers;
+import android.app.Activity;
import android.content.Context;
import android.view.View;
@SuppressWarnings("unused")
public class AdHelper {
- public static void initializeAds(View view, Context applicationContext, FragmentManager fragmentManager, String googleAppId, String adUnitId) {
+ public static void initializeAds(View view, Context applicationContext, Activity activity, FragmentManager fragmentManager, String adUnitId) {
// Do nothing because this is the standard flavor.
}
- public static void loadAd(View view, Context applicationContext, String adUnitId) {
+ public static void loadAd(View view, Context applicationContext, Activity activity, String adUnitId) {
// Do nothing because this is the standard flavor.
}
google()
}
dependencies {
- classpath 'com.android.tools.build:gradle:4.1.1'
+ classpath 'com.android.tools.build:gradle:4.1.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.21"
// NOTE: Do not place your application dependencies here; they belong