]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/commitdiff
Add an option to delete all domain settings at once. https://redmine.stoutner.com...
authorSoren Stoutner <soren@stoutner.com>
Tue, 29 Oct 2024 00:16:23 +0000 (17:16 -0700)
committerSoren Stoutner <soren@stoutner.com>
Tue, 29 Oct 2024 00:16:23 +0000 (17:16 -0700)
app/src/main/java/com/stoutner/privacybrowser/activities/DomainsActivity.kt
app/src/main/java/com/stoutner/privacybrowser/activities/ViewHeadersActivity.kt
app/src/main/java/com/stoutner/privacybrowser/dialogs/AboutViewHeadersDialog.kt
app/src/main/java/com/stoutner/privacybrowser/dialogs/AddDomainDialog.kt
app/src/main/java/com/stoutner/privacybrowser/dialogs/DeleteAllDomainsDialog.kt [new file with mode: 0644]
app/src/main/java/com/stoutner/privacybrowser/helpers/DomainsDatabaseHelper.kt
app/src/main/res/menu/domains_options_menu.xml
app/src/main/res/values-ru/strings.xml
app/src/main/res/values-tr/strings.xml
app/src/main/res/values/strings.xml

index 47ffe90f5872b6fb715ec72adcfc246544ac7497..f3b854e4424ef7915e401648851af7c07d16a1e9 100644 (file)
@@ -14,7 +14,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with Privacy Browser Android.  If not, see <http://www.gnu.org/licenses/>.
+ * along with Privacy Browser Android.  If not, see <https://www.gnu.org/licenses/>.
  */
 
 package com.stoutner.privacybrowser.activities
@@ -55,7 +55,7 @@ import com.google.android.material.snackbar.Snackbar
 
 import com.stoutner.privacybrowser.R
 import com.stoutner.privacybrowser.dialogs.AddDomainDialog
-import com.stoutner.privacybrowser.dialogs.AddDomainDialog.AddDomainListener
+import com.stoutner.privacybrowser.dialogs.DeleteAllDomainsDialog
 import com.stoutner.privacybrowser.fragments.DomainSettingsFragment
 import com.stoutner.privacybrowser.fragments.DomainsListFragment
 import com.stoutner.privacybrowser.helpers.DOMAIN_NAME
@@ -82,11 +82,12 @@ private const val DOMAIN_SETTINGS_DISPLAYED = "domain_settings_displayed"
 private const val DOMAIN_SETTINGS_SCROLL_Y = "domain_settings_scroll_y"
 private const val LISTVIEW_POSITION = "listview_position"
 
-class DomainsActivity : AppCompatActivity(), AddDomainListener, DomainsListFragment.DismissSnackbarInterface, DomainsListFragment.SaveDomainSettingsInterface {
+class DomainsActivity : AppCompatActivity(), AddDomainDialog.AddDomainListener, DeleteAllDomainsDialog.DeleteAllDomainSettingsListener, DomainsListFragment.DismissSnackbarInterface,
+    DomainsListFragment.SaveDomainSettingsInterface {
+
     companion object {
         // Define the public variables.
         var currentDomainDatabaseId = 0  // Used in `DomainsListFragment`.
-        var deleteMenuItemEnabled = true
         var dismissingSnackbar = false  // Used in `DomainsListFragment`.
         var domainsListViewPosition = 0  // Used in `DomainsListFragment`.
         var sslEndDateLong: Long = 0  // Used in `DomainsSettingsFragment`.
@@ -119,6 +120,8 @@ class DomainsActivity : AppCompatActivity(), AddDomainListener, DomainsListFragm
     private var closeActivityAfterDismissingSnackbar = false
     private var closeOnBack = false
     private var deletedDomainPosition = 0
+    private var deleteMenuItemEnabled = true
+    private var deleteAllMenuItemEnabled = true
     private var domainSettingsDatabaseIdBeforeRestart = 0
     private var domainSettingsDisplayedBeforeRestart = false
     private var domainSettingsScrollY = 0
@@ -274,22 +277,35 @@ class DomainsActivity : AppCompatActivity(), AddDomainListener, DomainsListFragm
                 // Inflate the menu.
                 menuInflater.inflate(R.menu.domains_options_menu, menu)
 
-                // Get a handle for the delete menu item.
+                // Get a handle for the menu items.
                 val deleteMenuItem = menu.findItem(R.id.delete_domain)
+                val deleteAllMenuItem = menu.findItem(R.id.delete_all)
 
                 // Get the domain settings fragment.
                 val domainSettingsFragment = supportFragmentManager.findFragmentByTag(DOMAIN_SETTINGS_FRAGMENT_TAG)
 
                 // Update the visibility of the delete menu item.
-                if (twoPanedMode)  // The device is in two-paned mode and a domain is selected.
+                if (twoPanedMode) {  // The device is in two-paned mode.
+                    // Show both menu items.
                     deleteMenuItem.isVisible = true
-                else if ((domainSettingsFragment != null) && domainSettingsFragment.isVisible)  // The device is in single-paned mode and the domain settings fragment is visible.
+                    deleteAllMenuItem.isVisible = true
+                } else if ((domainSettingsFragment != null) && domainSettingsFragment.isVisible) {  // The device is in single-paned mode and the domain settings fragment is visible.
+                    // Show the delete menu item.
                     deleteMenuItem.isVisible = true
-                else  // The device is in two-paned mode but no domain is selected (like after deleting a domain) or the device is in single-paned mode and the domains list is visible.
+
+                    // Hide the delete all menu item.
+                    deleteAllMenuItem.isVisible = false
+                } else {  // The device is in single-paned mode and the domains list is visible.
+                    // Hide the delete menu item.
                     deleteMenuItem.isVisible = false
 
+                    // Show the delete all menu item.
+                    deleteAllMenuItem.isVisible = true
+                }
+
                 // Update the status of the delete menu item.
                 deleteMenuItem.isEnabled = deleteMenuItemEnabled
+                deleteAllMenuItem.isEnabled = deleteAllMenuItemEnabled
             }
 
             override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
@@ -371,9 +387,6 @@ class DomainsActivity : AppCompatActivity(), AddDomainListener, DomainsListFragm
                                 // Store the deleted domain position, which is needed if undo is selected in the snackbar.
                                 deletedDomainPosition = domainsListView!!.checkedItemPosition
 
-                                // Disable the delete menu item.
-                                deleteMenuItemEnabled = false
-
                                 // Get a handle for the domain settings fragment.
                                 val domainSettingsFragment = supportFragmentManager.findFragmentById(R.id.domain_settings_fragment_container)!!
 
@@ -385,10 +398,6 @@ class DomainsActivity : AppCompatActivity(), AddDomainListener, DomainsListFragm
 
                                 // Disable the delete menu item.
                                 deleteMenuItemEnabled = false
-
-                                // Invalidate the options menu.
-                                invalidateMenu()
-
                             } else {  // Single-paned mode.
                                 // Instantiate a new domains list fragment.
                                 val domainsListFragment = DomainsListFragment()
@@ -405,24 +414,8 @@ class DomainsActivity : AppCompatActivity(), AddDomainListener, DomainsListFragm
                             // Get a cursor that does not show the domain to be deleted.
                             val domainsPendingDeleteCursor = domainsDatabaseHelper.getDomainNameCursorOrderedByDomainExcept(databaseIdToDelete)
 
-                            // Setup the domains pending delete cursor adapter.
-                            val domainsPendingDeleteCursorAdapter: CursorAdapter = object : CursorAdapter(applicationContext, domainsPendingDeleteCursor, false) {
-                                override fun newView(context: Context, cursor: Cursor, parent: ViewGroup): View {
-                                    // Inflate the individual item layout.
-                                    return layoutInflater.inflate(R.layout.domain_name_linearlayout, parent, false)
-                                }
-
-                                override fun bindView(view: View, context: Context, cursor: Cursor) {
-                                    // Get the domain name string.
-                                    val domainNameString = cursor.getString(cursor.getColumnIndexOrThrow(DOMAIN_NAME))
-
-                                    // Get a handle for the domain name text view.
-                                    val domainNameTextView = view.findViewById<TextView>(R.id.domain_name_textview)
-
-                                    // Display the domain name.
-                                    domainNameTextView.text = domainNameString
-                                }
-                            }
+                            // Populate the domains pending delete cursor adapter.
+                            val domainsPendingDeleteCursorAdapter = populateDomainsCursorAdapter(domainsPendingDeleteCursor)
 
                             // Update the handle for the current domains list view.
                             domainsListView = findViewById(R.id.domains_listview)
@@ -430,6 +423,12 @@ class DomainsActivity : AppCompatActivity(), AddDomainListener, DomainsListFragm
                             // Update the list view.
                             domainsListView!!.adapter = domainsPendingDeleteCursorAdapter
 
+                            // Disable the delete all menu item if no domains are displayed.
+                            deleteAllMenuItemEnabled = (domainsPendingDeleteCursor.count > 0)
+
+                            // Invalidate the options menu.
+                            invalidateMenu()
+
                             // Display a snackbar.
                             undoDeleteSnackbar = Snackbar.make(domainsListView!!, R.string.domain_deleted, Snackbar.LENGTH_LONG)
                                 .setAction(R.string.undo) {}  // Do nothing because everything will be handled by `onDismissed()` below.
@@ -440,7 +439,7 @@ class DomainsActivity : AppCompatActivity(), AddDomainListener, DomainsListFragm
                                             // Create an arguments bundle.
                                             val argumentsBundle = Bundle()
 
-                                            // Store the domains settings in the arguments bundle.
+                                            // Store the domain settings in the arguments bundle.
                                             argumentsBundle.putInt(DomainSettingsFragment.DATABASE_ID, databaseIdToDelete)
                                             argumentsBundle.putInt(DomainSettingsFragment.SCROLL_Y, domainSettingsScrollY)
 
@@ -451,28 +450,12 @@ class DomainsActivity : AppCompatActivity(), AddDomainListener, DomainsListFragm
                                             domainSettingsFragment.arguments = argumentsBundle
 
                                             // Display the correct fragments.
-                                            if (twoPanedMode) {  // The device in in two-paned mode.
+                                            if (twoPanedMode) {  // The device is in two-paned mode.
                                                 // Get a cursor with the current contents of the domains database.
                                                 val undoDeleteDomainsCursor = domainsDatabaseHelper.domainNameCursorOrderedByDomain
 
-                                                // Setup the domains cursor adapter.
-                                                val undoDeleteDomainsCursorAdapter: CursorAdapter = object : CursorAdapter(applicationContext, undoDeleteDomainsCursor, false) {
-                                                    override fun newView(context: Context, cursor: Cursor, parent: ViewGroup): View {
-                                                        // Inflate the individual item layout.
-                                                        return layoutInflater.inflate(R.layout.domain_name_linearlayout, parent, false)
-                                                    }
-
-                                                    override fun bindView(view: View, context: Context, cursor: Cursor) {
-                                                        /// Get the domain name string.
-                                                        val domainNameString = cursor.getString(cursor.getColumnIndexOrThrow(DOMAIN_NAME))
-
-                                                        // Get a handle for the domain name text view.
-                                                        val domainNameTextView = view.findViewById<TextView>(R.id.domain_name_textview)
-
-                                                        // Display the domain name.
-                                                        domainNameTextView.text = domainNameString
-                                                    }
-                                                }
+                                                // Populate the undo delete domains cursor adapter.
+                                                val undoDeleteDomainsCursorAdapter = populateDomainsCursorAdapter(undoDeleteDomainsCursor)
 
                                                 // Update the domains list view.
                                                 domainsListView!!.adapter = undoDeleteDomainsCursorAdapter
@@ -480,14 +463,15 @@ class DomainsActivity : AppCompatActivity(), AddDomainListener, DomainsListFragm
                                                 // Select the previously deleted domain in the list view.
                                                 domainsListView!!.setItemChecked(deletedDomainPosition, true)
 
-                                                // Enable the delete menu item.
+                                                // Enable the menu items.
                                                 deleteMenuItemEnabled = true
+                                                deleteAllMenuItemEnabled = true
 
                                                 // Display the domain settings fragment.
                                                 supportFragmentManager.commitNow {
                                                     replace(R.id.domain_settings_fragment_container, domainSettingsFragment, DOMAIN_SETTINGS_FRAGMENT_TAG)
                                                 }
-                                            } else {  // The device in in one-paned mode.
+                                            } else {  // The device is in one-paned mode.
                                                 // Hide the add domain floating action button.
                                                 addDomainFAB.hide()
 
@@ -529,6 +513,17 @@ class DomainsActivity : AppCompatActivity(), AddDomainListener, DomainsListFragm
                             undoDeleteSnackbar!!.show()
                         }
                     }
+
+                    R.id.delete_all -> {  // Delete all.
+                        // Instantiate the delete all domains dialog fragment.
+                        val deleteAllDomainsDialogFragment = DeleteAllDomainsDialog()
+
+                        // Show the delete all domains alert dialog.
+                        deleteAllDomainsDialogFragment.show(supportFragmentManager, getString(R.string.delete_all))
+
+                        // Consume the event.
+                        return true
+                    }
                 }
 
                 // Consume the event.
@@ -735,6 +730,130 @@ class DomainsActivity : AppCompatActivity(), AddDomainListener, DomainsListFragm
         }
     }
 
+    override fun deleteAllDomainSettings() {
+        // Reset close-on-back, which otherwise can cause errors if the system attempts to save settings for a domain that no longer exists.
+        closeOnBack = false
+
+        // Store a copy of the currently selected domain database ID because it could change while the snackbar is displayed.
+        val currentlySelectedDatabaseId = currentDomainDatabaseId
+
+        // Store the domains list view scroll position.
+        val domainsListViewFirstVisiblePosition = domainsListView!!.firstVisiblePosition
+
+        // Update the fragments and menu items.
+        if (twoPanedMode) {  // Two-paned mode.
+            // Store the deleted domain position, which is needed if undo is selected in the snackbar.
+            deletedDomainPosition = domainsListView!!.checkedItemPosition
+
+            // Get a handle for the domain settings fragment.
+            val domainSettingsFragment = supportFragmentManager.findFragmentById(R.id.domain_settings_fragment_container)!!
+
+            // Get a handle for the domain settings fragment view.
+            val domainSettingsFragmentView = domainSettingsFragment.requireView()
+
+            // Hide the domain settings fragment.
+            domainSettingsFragmentView.visibility = View.INVISIBLE
+
+            // Disable the delete menu item.
+            deleteMenuItemEnabled = false
+        }
+
+        // Disable the delete all menu item.
+        deleteAllMenuItemEnabled = false
+
+        // Invalidate the options menu.
+        invalidateMenu()
+
+        // Create an empty cursor
+        val emptyCursor = domainsDatabaseHelper.getCursorForId(-1)
+
+        // Populate the empty cursor adapter.
+        val emptyCursorAdapter = populateDomainsCursorAdapter(emptyCursor)
+
+        // Update the handle for the current domains list view.
+        domainsListView = findViewById(R.id.domains_listview)
+
+        // Update the list view.
+        domainsListView!!.adapter = emptyCursorAdapter
+
+        // Get a handle for the activity (used in an inner class below).
+        val activity: Activity = this
+
+        // Display a snackbar.
+        undoDeleteSnackbar = Snackbar.make(domainsListView!!, R.string.all_domains_deleted, Snackbar.LENGTH_LONG)
+            .setAction(R.string.undo) {}  // Do nothing because everything will be handled by `onDismissed()` below.
+            .addCallback(object : Snackbar.Callback() {
+                override fun onDismissed(snackbar: Snackbar, event: Int) {
+                    //Run commands based on the event.
+                    if (event == DISMISS_EVENT_ACTION) {  // The user pushed the `Undo` button.
+                        // Get a cursor with the current contents of the domains database.
+                        val undoDeleteDomainsCursor = domainsDatabaseHelper.domainNameCursorOrderedByDomain
+
+                        // Populate the undo delete domains cursor adapter.
+                        val undoDeleteDomainsCursorAdapter = populateDomainsCursorAdapter(undoDeleteDomainsCursor)
+
+                        // Update the domains list view.
+                        domainsListView!!.adapter = undoDeleteDomainsCursorAdapter
+
+                        // Redisplay the domain settings in two-paned mode.
+                        if (twoPanedMode) {
+                            // Create an arguments bundle.
+                            val argumentsBundle = Bundle()
+
+                            // Store the domain settings in the arguments bundle.
+                            argumentsBundle.putInt(DomainSettingsFragment.DATABASE_ID, currentlySelectedDatabaseId)
+                            argumentsBundle.putInt(DomainSettingsFragment.SCROLL_Y, domainSettingsScrollY)
+
+                            // Instantiate a new domain settings fragment.
+                            val domainSettingsFragment = DomainSettingsFragment()
+
+                            // Add the arguments bundle to the domain settings fragment.
+                            domainSettingsFragment.arguments = argumentsBundle
+
+                            // Select the previously selected domain in the list view.
+                            domainsListView!!.setItemChecked(deletedDomainPosition, true)
+
+                            // Display the domain settings fragment.
+                            supportFragmentManager.commitNow {
+                                replace(R.id.domain_settings_fragment_container, domainSettingsFragment, DOMAIN_SETTINGS_FRAGMENT_TAG)
+                            }
+
+                            // Enable the delete menu item.
+                            deleteMenuItemEnabled = true
+                        }
+
+                        // Restore the domains list view scroll position.
+                        domainsListView!!.setSelection(domainsListViewFirstVisiblePosition)
+
+                        // Enable the delete all menu item.
+                        deleteAllMenuItemEnabled = true
+
+                        // Update the options menu.
+                        invalidateMenu()
+                    } else {  // The snackbar was dismissed without the undo button being pushed.
+                        // Delete all the domains.
+                        val rowsDeleted = domainsDatabaseHelper.deleteAllDomains()
+
+                        // Reset the dismissing snackbar option.
+                        // The rows deleted should always be greater than 0, but in all cases they should be greater than -1.
+                        // This has the effect of tricking the compiler into waiting until after the delete finishes to reenable the delete menu item,
+                        // because the compiler (probably) can't tell that the response will never be less than -1, so it doesn't compile out the delay.
+                        if (rowsDeleted > -1) {
+                            // Reset the dismissing snackbar tracker.
+                            dismissingSnackbar = false
+                        }
+
+                        // Close the activity if back was pressed.
+                        if (closeActivityAfterDismissingSnackbar)
+                            NavUtils.navigateUpFromSameTask(activity)
+                    }
+                }
+            })
+
+        // Show the Snackbar.
+        undoDeleteSnackbar!!.show()
+    }
+
     override fun saveDomainSettings(view: View) {
         // Get handles for the domain settings.
         val domainNameEditText = view.findViewById<EditText>(R.id.domain_settings_name_edittext)
@@ -821,11 +940,8 @@ class DomainsActivity : AppCompatActivity(), AddDomainListener, DomainsListFragm
             domainsDatabaseHelper.updatePinnedIpAddresses(currentDomainDatabaseId, currentIpAddresses!!)
     }
 
-    private fun populateDomainsListView(highlightedDomainDatabaseId: Int, domainsListViewPosition: Int) {
-        // Get a cursor with the current contents of the domains database.
-        val domainsCursor = domainsDatabaseHelper.domainNameCursorOrderedByDomain
-
-        // Setup the domains cursor adapter.
+    private fun populateDomainsCursorAdapter(domainsCursor: Cursor) :CursorAdapter {
+        // Populate the domains cursor adapter.
         val domainsCursorAdapter: CursorAdapter = object : CursorAdapter(applicationContext, domainsCursor, false) {
             override fun newView(context: Context, cursor: Cursor, parent: ViewGroup): View {
                 // Inflate the individual item layout.
@@ -833,17 +949,28 @@ class DomainsActivity : AppCompatActivity(), AddDomainListener, DomainsListFragm
             }
 
             override fun bindView(view: View, context: Context, cursor: Cursor) {
+                /// Get the domain name string.
+                val domainNameString = cursor.getString(cursor.getColumnIndexOrThrow(DOMAIN_NAME))
+
                 // Get a handle for the domain name text view.
                 val domainNameTextView = view.findViewById<TextView>(R.id.domain_name_textview)
 
-                // Get the domain name string.
-                val domainNameString = cursor.getString(cursor.getColumnIndexOrThrow(DOMAIN_NAME))
-
-                // Set the domain name.
+                // Display the domain name.
                 domainNameTextView.text = domainNameString
             }
         }
 
+        // Return the domains cursor adapter.
+        return domainsCursorAdapter
+    }
+
+    private fun populateDomainsListView(highlightedDomainDatabaseId: Int, domainsListViewPosition: Int) {
+        // Get a cursor with the current contents of the domains database.
+        val domainsCursor = domainsDatabaseHelper.domainNameCursorOrderedByDomain
+
+        // Populate the domains cursor adapter.
+        val domainsCursorAdapter = populateDomainsCursorAdapter(domainsCursor)
+
         // get a handle for the current domains listview.
         domainsListView = findViewById(R.id.domains_listview)
 
@@ -898,18 +1025,20 @@ class DomainsActivity : AppCompatActivity(), AddDomainListener, DomainsListFragm
                 replace(R.id.domain_settings_fragment_container, domainSettingsFragment, DOMAIN_SETTINGS_FRAGMENT_TAG)
             }
 
-            // Enable the delete options menu item.
+            // Enable the menu items.
             deleteMenuItemEnabled = true
-
-            // Update the options menu.
-            invalidateMenu()
+            deleteAllMenuItemEnabled = true
         } else if (twoPanedMode) {  // Two-paned mode is enabled but there are no domains.
-            // Disable the delete menu item.
+            // Disable the menu items.
             deleteMenuItemEnabled = false
-
-            // Update the options menu.
-            invalidateMenu()
+            deleteAllMenuItemEnabled = false
+        } else {  // Single-paned mode is enabled and the domains list is displayed.
+            // Set the delete all menu item status according to the number of domains.
+            deleteAllMenuItemEnabled = (domainsCursor.count > 0)
         }
+
+        // Update the options menu.
+        invalidateMenu()
     }
 
     override fun dismissSnackbar() {
index 8e72b596b9cea383b7d6a0b509b71e03030e4060..77b3aef5df7abcafd104d6d4b2d0a893f9f23995 100644 (file)
@@ -14,7 +14,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with Privacy Browser Android.  If not, see <http://www.gnu.org/licenses/>.
+ * along with Privacy Browser Android.  If not, see <https://www.gnu.org/licenses/>.
  */
 
 package com.stoutner.privacybrowser.activities
index f88da7b0741494b47bf910e478c38608a4a2c862..a03a7faaabd5fd43680f0d08282a6f646f75337b 100644 (file)
@@ -14,7 +14,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with Privacy Browser Android.  If not, see <http://www.gnu.org/licenses/>.
+ * along with Privacy Browser Android.  If not, see <https://www.gnu.org/licenses/>.
  */
 
 package com.stoutner.privacybrowser.dialogs
index be5af10b33e2e4bf83c4700ab2c41669414de0d4..be337f603eb80e2cea36a10703a1a910cc607f99 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright 2017-2024 Soren Stoutner <soren@stoutner.com>.
  *
- * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
+ * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android/>.
  *
  * Privacy Browser Android is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -14,7 +14,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with Privacy Browser Android.  If not, see <http://www.gnu.org/licenses/>.
+ * along with Privacy Browser Android.  If not, see <https://www.gnu.org/licenses/>.
  */
 
 package com.stoutner.privacybrowser.dialogs
diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/DeleteAllDomainsDialog.kt b/app/src/main/java/com/stoutner/privacybrowser/dialogs/DeleteAllDomainsDialog.kt
new file mode 100644 (file)
index 0000000..93a6f19
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2018-2024 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android/>.
+ *
+ * Privacy Browser Android 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 Android 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 Android.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package com.stoutner.privacybrowser.dialogs
+
+import android.app.Dialog
+import android.content.Context
+import android.content.DialogInterface
+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
+
+class DeleteAllDomainsDialog : DialogFragment() {
+    // Declare the class variables
+    private lateinit var deleteAllDomainSettingsListener: DeleteAllDomainSettingsListener
+
+    // The public interface is used to send information back to the parent activity.
+    interface DeleteAllDomainSettingsListener {
+        fun deleteAllDomainSettings()
+    }
+
+    override fun onAttach(context: Context) {
+        // Run the default commands.
+        super.onAttach(context)
+
+        // Get a handle for the delete all domain settings listener from the launching context.
+        deleteAllDomainSettingsListener = context as DeleteAllDomainSettingsListener
+    }
+
+    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+        // Use a builder to create the alert dialog.
+        val dialogBuilder = AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog)
+
+        // Set the icon according to the theme.
+        dialogBuilder.setIcon(R.drawable.delete)
+
+        // Set the title.
+        dialogBuilder.setTitle(R.string.delete_all)
+
+        // Set the text.
+        dialogBuilder.setMessage(R.string.delete_all_domain_settings)
+
+        // Set the negative button.  Using `null` as the listener closes the dialog without doing anything else.
+        dialogBuilder.setNegativeButton(R.string.cancel, null)
+
+        // Set the positive button.
+        dialogBuilder.setPositiveButton(R.string.delete, { _: DialogInterface, _: Int ->
+            // Delete all the domain settings.
+            deleteAllDomainSettingsListener.deleteAllDomainSettings()
+        })
+
+        // Create an alert dialog from the alert dialog builder.
+        val alertDialog = dialogBuilder.create()
+
+        // Get a handle for the shared preferences.
+        val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
+
+        // 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
+    }
+}
index 38ce8992fcac52ee2a35d680aaacf69ac7525383..4ff218489227a639b09c3046511866447cde616d 100644 (file)
@@ -14,7 +14,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with Privacy Browser Android.  If not, see <http://www.gnu.org/licenses/>.
+ * along with Privacy Browser Android.  If not, see <https://www.gnu.org/licenses/>.
  */
 
 package com.stoutner.privacybrowser.helpers
@@ -532,6 +532,20 @@ class DomainsDatabaseHelper(private val appContext: Context) : SQLiteOpenHelper(
         domainsDatabase.close()
     }
 
+    fun deleteAllDomains() : Int {
+        // Get a writable database handle.
+        val domainsDatabase = this.writableDatabase
+
+        // Delete the row for the specified database ID.
+        val rowsDeleted = domainsDatabase.delete(DOMAINS_TABLE, "", null)
+
+        // Close the database handle.
+        domainsDatabase.close()
+
+        // Return the delete status.
+        return rowsDeleted
+    }
+
     fun deleteDomain(databaseId: Int) : Int {
         // Get a writable database handle.
         val domainsDatabase = this.writableDatabase
index 37427c85143c240d5b7ce60bb07d17f7a855b31b..1bb6d20d76f706ea5277d0f1a01fc00d46b456a0 100644 (file)
@@ -16,7 +16,7 @@
   GNU General Public License for more details.
 
   You should have received a copy of the GNU General Public License
-  along with Privacy Browser Android.  If not, see <http://www.gnu.org/licenses/>. -->
+  along with Privacy Browser Android.  If not, see <https://www.gnu.org/licenses/>. -->
 
 <menu
     xmlns:android="http://schemas.android.com/apk/res/android"
         android:orderInCategory="10"
         android:icon="@drawable/delete"
         app:showAsAction="ifRoom" />
+
+    <item
+        android:id="@+id/delete_all"
+        android:title="@string/delete_all"
+        android:orderInCategory="20"
+        app:showAsAction="never" />
 </menu>
index ab040d02e2b223552b3ed8f2f6bd186c7a6ce9d9..ed71aba35b800005ac28e417d4136986ec987b54 100644 (file)
@@ -3,7 +3,7 @@
 <!--
   Copyright 2015-2024 Soren Stoutner <soren@stoutner.com>.
 
-  This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
+  This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android/>.
 
   Privacy Browser Android is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -16,7 +16,7 @@
   GNU General Public License for more details.
 
   You should have received a copy of the GNU General Public License
-  along with Privacy Browser Android.  If not, see <http://www.gnu.org/licenses/>. -->
+  along with Privacy Browser Android.  If not, see <https://www.gnu.org/licenses/>. -->
 
 <resources>
     <!-- Activities. -->
     <string name="selected">выбраны:\u0020 %1$d</string>
     <string name="move_up">Вверх</string>
     <string name="move_down">Вниз</string>
+    <string name="move_to_top">Переместить вверх</string>
+    <string name="move_to_bottom">Переместить вниз</string>
     <string name="edit">Изменить</string>
     <string name="delete">Удалить</string>
     <string name="select_all">Выбрать все</string>
index 45658be00ab78a2d005621593f9a6401ce3b5619..358bf36765483a90815d1460dec11cd31902e656 100644 (file)
@@ -3,7 +3,7 @@
 <!--
   Copyright 2015-2024 Soren Stoutner <soren@stoutner.com>.
 
-  This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
+  This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android/>.
 
   Privacy Browser Android is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -16,7 +16,7 @@
   GNU General Public License for more details.
 
   You should have received a copy of the GNU General Public License
-  along with Privacy Browser Android.  If not, see <http://www.gnu.org/licenses/>. -->
+  along with Privacy Browser Android.  If not, see <https://www.gnu.org/licenses/>. -->
 
 <resources>
     <!-- Activities. -->
index ca05c27647e8d0aa86bdceb23a9fce8784d015d4..20b18f864faef2b1dd6b98fcce6a3b76258a77df 100644 (file)
     <string name="add_domain">Add Domain</string>
     <string name="domain_name_already_exists">Domain name already exists</string>
     <string name="add">Add</string>
+    <string name="delete_all">Delete All</string>
+    <string name="delete_all_domain_settings">Delete all domain settings?</string>
     <string name="domain_name">Domain name</string>
     <string name="domain_deleted">Domain deleted</string>
+    <string name="all_domains_deleted">All domains deleted</string>
     <string name="domain_name_instructions">*. may be prepended to a domain to include all subdomains (eg. *.stoutner.com)</string>
     <string-array name="javascript_array">
         <item>System default</item>