From ae28ebe701ad0cb4c034822019f1508b8f8af1f7 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Thu, 14 Nov 2019 09:59:55 +0100 Subject: [PATCH 1/3] Add dialog when permanently delete nodes --- .../keepass/activities/GroupActivity.kt | 41 +++++--- .../dialogs/DeleteNodesDialogFragment.kt | 93 +++++++++++++++++++ .../keepass/database/element/PwDatabaseV4.kt | 2 +- .../DatabaseTaskNotificationService.kt | 12 +-- app/src/main/res/values/strings.xml | 1 + 5 files changed, 131 insertions(+), 18 deletions(-) create mode 100644 app/src/main/java/com/kunzisoft/keepass/activities/dialogs/DeleteNodesDialogFragment.kt diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt index 1cf9d7771..f8e99ba5b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt @@ -42,10 +42,7 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.fragment.app.FragmentManager import com.google.android.material.snackbar.Snackbar import com.kunzisoft.keepass.R -import com.kunzisoft.keepass.activities.dialogs.GroupEditDialogFragment -import com.kunzisoft.keepass.activities.dialogs.IconPickerDialogFragment -import com.kunzisoft.keepass.activities.dialogs.ReadOnlyDialog -import com.kunzisoft.keepass.activities.dialogs.SortDialogFragment +import com.kunzisoft.keepass.activities.dialogs.* import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper import com.kunzisoft.keepass.activities.lock.LockingActivity @@ -77,6 +74,7 @@ class GroupActivity : LockingActivity(), IconPickerDialogFragment.IconPickerListener, ListNodesFragment.NodeClickListener, ListNodesFragment.NodesActionMenuListener, + DeleteNodesDialogFragment.DeleteNodeListener, ListNodesFragment.OnScrollListener, SortDialogFragment.SortSelectionListener { @@ -245,13 +243,17 @@ class GroupActivity : LockingActivity(), // Add trash in views list if it doesn't exists if (database.isRecycleBinEnabled) { val recycleBin = database.recycleBin - if (mCurrentGroup != null && recycleBin != null - && mCurrentGroup!!.parent == null - && mCurrentGroup != recycleBin) { - if (mListNodesFragment?.contains(recycleBin) == true) + val currentGroup = mCurrentGroup + if (currentGroup != null && recycleBin != null + && currentGroup != recycleBin) { + // Recycle bin already here, simply update it + if (mListNodesFragment?.contains(recycleBin) == true) { mListNodesFragment?.updateNode(recycleBin) - else + } + // Recycle bin not here, verify if parents are similar to add it + else if (currentGroup.parent == recycleBin.parent) { mListNodesFragment?.addNode(recycleBin) + } } } } @@ -596,12 +598,29 @@ class GroupActivity : LockingActivity(), } override fun onDeleteMenuClick(nodes: List): Boolean { + val database = mDatabase + if (database != null + && database.isRecycleBinEnabled + && database.recycleBin != mCurrentGroup) { + // If recycle bin enabled and not in recycle bin, move in recycle bin + progressDialogThread?.startDatabaseDeleteNodes( + nodes, + !mReadOnly + ) + } else { + // open the dialog to confirm deletion + DeleteNodesDialogFragment.getInstance(nodes) + .show(supportFragmentManager, "deleteNodesDialogFragment") + } + finishNodeAction() + return true + } + + override fun permanentlyDeleteNodes(nodes: List) { progressDialogThread?.startDatabaseDeleteNodes( nodes, !mReadOnly ) - finishNodeAction() - return true } override fun onResume() { diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/DeleteNodesDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/DeleteNodesDialogFragment.kt new file mode 100644 index 000000000..bf3d7291a --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/DeleteNodesDialogFragment.kt @@ -0,0 +1,93 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX 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. + * + * KeePass DX 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 KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.activities.dialogs + +import android.app.Dialog +import android.content.Context +import android.os.Bundle +import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.DialogFragment +import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.database.element.Database +import com.kunzisoft.keepass.database.element.NodeVersioned +import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService +import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.getBundleFromListNodes +import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.getListNodesFromBundle + +class DeleteNodesDialogFragment : DialogFragment() { + + private var mNodesToDelete: List = ArrayList() + private var mListener: DeleteNodeListener? = null + + override fun onAttach(context: Context) { + super.onAttach(context) + try { + mListener = context as DeleteNodeListener + } catch (e: ClassCastException) { + throw ClassCastException(context.toString() + + " must implement " + DeleteNodeListener::class.java.name) + } + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + + arguments?.apply { + if (containsKey(DatabaseTaskNotificationService.GROUPS_ID_KEY) + && containsKey(DatabaseTaskNotificationService.ENTRIES_ID_KEY)) { + mNodesToDelete = getListNodesFromBundle(Database.getInstance(), this) + } + } ?: savedInstanceState?.apply { + if (containsKey(DatabaseTaskNotificationService.GROUPS_ID_KEY) + && containsKey(DatabaseTaskNotificationService.ENTRIES_ID_KEY)) { + mNodesToDelete = getListNodesFromBundle(Database.getInstance(), savedInstanceState) + } + } + activity?.let { activity -> + // Use the Builder class for convenient dialog construction + val builder = AlertDialog.Builder(activity) + + builder.setMessage(getString(R.string.warning_permanently_delete_nodes)) + builder.setPositiveButton(android.R.string.yes) { _, _ -> + mListener?.permanentlyDeleteNodes(mNodesToDelete) + } + builder.setNegativeButton(android.R.string.no) { _, _ -> dismiss() } + // Create the AlertDialog object and return it + return builder.create() + } + return super.onCreateDialog(savedInstanceState) + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putAll(getBundleFromListNodes(mNodesToDelete)) + } + + interface DeleteNodeListener { + fun permanentlyDeleteNodes(nodes: List) + } + + companion object { + fun getInstance(nodesToDelete: List): DeleteNodesDialogFragment { + return DeleteNodesDialogFragment().apply { + arguments = getBundleFromListNodes(nodesToDelete) + } + } + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.kt index 06221e87a..f4e45e396 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.kt @@ -422,7 +422,7 @@ class PwDatabaseV4 : PwDatabase { if (!isRecycleBinEnabled) return false if (recycleBin == null) - return true + return true // TODO Create recycle bin if (!node.isContainedIn(recycleBin!!)) return true return false diff --git a/app/src/main/java/com/kunzisoft/keepass/notifications/DatabaseTaskNotificationService.kt b/app/src/main/java/com/kunzisoft/keepass/notifications/DatabaseTaskNotificationService.kt index 634baef94..b7e2ced07 100644 --- a/app/src/main/java/com/kunzisoft/keepass/notifications/DatabaseTaskNotificationService.kt +++ b/app/src/main/java/com/kunzisoft/keepass/notifications/DatabaseTaskNotificationService.kt @@ -509,23 +509,23 @@ class DatabaseTaskNotificationService : NotificationService(), ProgressTaskUpdat } fun getBundleFromListNodes(nodes: List): Bundle { - val groupsIdToCopy = ArrayList>() - val entriesIdToCopy = ArrayList>() + val groupsId = ArrayList>() + val entriesId = ArrayList>() nodes.forEach { nodeVersioned -> when (nodeVersioned.type) { Type.GROUP -> { (nodeVersioned as GroupVersioned).nodeId?.let { groupId -> - groupsIdToCopy.add(groupId) + groupsId.add(groupId) } } Type.ENTRY -> { - entriesIdToCopy.add((nodeVersioned as EntryVersioned).nodeId) + entriesId.add((nodeVersioned as EntryVersioned).nodeId) } } } return Bundle().apply { - putParcelableArrayList(GROUPS_ID_KEY, groupsIdToCopy) - putParcelableArrayList(ENTRIES_ID_KEY, entriesIdToCopy) + putParcelableArrayList(GROUPS_ID_KEY, groupsId) + putParcelableArrayList(ENTRIES_ID_KEY, entriesId) } } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 271c18ac1..001287855 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -242,6 +242,7 @@ Mount the SD card to create or load a database. Do you really want no password unlocking protection? Are you sure you do not want to use any encryption key? + Are you sure you want to permanently delete the selected nodes? Version %1$s Build %1$s Biometric prompt is supported but not set up. From f54bac15c929a8f89965830603cb63e173601763 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Thu, 14 Nov 2019 13:14:02 +0100 Subject: [PATCH 2/3] Add recycle bin setting and fix delete node --- .../keepass/activities/GroupActivity.kt | 9 +---- .../keepass/activities/ListNodesFragment.kt | 10 ++--- .../keepass/database/SortNodeEnum.kt | 8 ++-- .../database/action/ProgressDialogThread.kt | 11 ++++++ .../keepass/database/element/Database.kt | 14 ++++++- .../keepass/database/element/PwDatabaseV4.kt | 17 +++++--- .../DatabaseTaskNotificationService.kt | 16 +++++++- .../settings/NestedSettingsFragment.kt | 39 +++++++++++++++++-- app/src/main/res/values-fr/strings.xml | 2 +- app/src/main/res/values/donottranslate.xml | 3 +- app/src/main/res/values/strings.xml | 3 +- app/src/main/res/xml/preferences_database.xml | 6 ++- 12 files changed, 106 insertions(+), 32 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt index f8e99ba5b..48ae8fbc5 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt @@ -232,13 +232,8 @@ class GroupActivity : LockingActivity(), ACTION_DATABASE_DELETE_NODES_TASK -> { if (result.isSuccess) { - // Rebuild all the list the avoid bug when delete node from db sort - if (PreferencesUtil.getListSort(this@GroupActivity) == SortNodeEnum.DB) { - mListNodesFragment?.rebuildList() - } else { - // Use the old Nodes / entries unchanged with the old parent - mListNodesFragment?.removeNodes(oldNodes) - } + // Rebuild all the list the avoid bug when delete node from sort + mListNodesFragment?.rebuildList() // Add trash in views list if it doesn't exists if (database.isRecycleBinEnabled) { diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.kt index 470034783..d56b8f912 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.kt @@ -228,8 +228,7 @@ class ListNodesFragment : StylishFragment(), SortDialogFragment.SortSelectionLis R.id.menu_sort -> { context?.let { context -> val sortDialogFragment: SortDialogFragment = - if (Database.getInstance().allowRecycleBin - && Database.getInstance().isRecycleBinEnabled) { + if (Database.getInstance().isRecycleBinEnabled) { SortDialogFragment.getInstance( PreferencesUtil.getListSort(context), PreferencesUtil.getAscendingSort(context), @@ -276,7 +275,8 @@ class ListNodesFragment : StylishFragment(), SortDialogFragment.SortSelectionLis // Open and Edit for a single item if (nodes.size == 1) { // Edition - if (readOnly || nodes[0] == database.recycleBin) { + if (readOnly + || (database.isRecycleBinEnabled && nodes[0] == database.recycleBin)) { menu?.removeItem(R.id.menu_edit) } } else { @@ -287,7 +287,6 @@ class ListNodesFragment : StylishFragment(), SortDialogFragment.SortSelectionLis // Copy and Move (not for groups) if (readOnly || isASearchResult - || nodes.any { it == database.recycleBin } || nodes.any { it.type == Type.GROUP }) { // TODO COPY For Group menu?.removeItem(R.id.menu_copy) @@ -295,7 +294,8 @@ class ListNodesFragment : StylishFragment(), SortDialogFragment.SortSelectionLis } // Deletion - if (readOnly || nodes.any { it == database.recycleBin }) { + if (readOnly + || (database.isRecycleBinEnabled && nodes.any { it == database.recycleBin })) { menu?.removeItem(R.id.menu_delete) } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.kt b/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.kt index 7caddbe8e..1dc28111c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.kt @@ -59,13 +59,13 @@ enum class SortNodeEnum { if (object1.type == Type.GROUP) { return if (object2.type == Type.GROUP) { // RecycleBin at end of groups - if (recycleBinBottom) { - if (Database.getInstance().recycleBin == object1) + val database = Database.getInstance() + if (database.isRecycleBinEnabled && recycleBinBottom) { + if (database.recycleBin == object1) return 1 - if (Database.getInstance().recycleBin == object2) + if (database.recycleBin == object2) return -1 } - specificOrderOrHashIfEquals(object1, object2) } else if (object2.type == Type.ENTRY) { if (groupsBefore) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/ProgressDialogThread.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/ProgressDialogThread.kt index 8e4beb7e5..a5f2ccc77 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/ProgressDialogThread.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/ProgressDialogThread.kt @@ -20,6 +20,7 @@ import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Compa import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_DELETE_NODES_TASK import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_LOAD_TASK import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_MOVE_NODES_TASK +import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_COLOR_TASK import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_COMPRESSION_TASK import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_DEFAULT_USERNAME_TASK @@ -461,4 +462,14 @@ class ProgressDialogThread(private val activity: FragmentActivity, } , ACTION_DATABASE_SAVE_PARALLELISM_TASK) } + + /** + * Save Database without parameter + */ + fun startDatabaseSave(save: Boolean) { + start(Bundle().apply { + putBoolean(DatabaseTaskNotificationService.SAVE_DATABASE_KEY, save) + } + , ACTION_DATABASE_SAVE) + } } \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt index d43994ff3..3c488e007 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt @@ -24,7 +24,6 @@ import android.content.res.Resources import android.database.Cursor import android.net.Uri import android.util.Log -import android.webkit.URLUtil import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine import com.kunzisoft.keepass.database.NodeHandler import com.kunzisoft.keepass.database.cursor.EntryCursorV3 @@ -253,8 +252,11 @@ class Database { val allowRecycleBin: Boolean get() = pwDatabaseV4 != null - val isRecycleBinEnabled: Boolean + var isRecycleBinEnabled: Boolean get() = pwDatabaseV4?.isRecycleBinEnabled ?: false + set(value) { + pwDatabaseV4?.isRecycleBinEnabled = value + } val recycleBin: GroupVersioned? get() { @@ -264,6 +266,14 @@ class Database { return null } + fun ensureRecycleBinExists(resources: Resources) { + pwDatabaseV4?.ensureRecycleBinExists(resources) + } + + fun removeRecycleBin() { + pwDatabaseV4?.removeRecycleBin() + } + private fun setDatabaseV3(pwDatabaseV3: PwDatabaseV3) { this.pwDatabaseV3 = pwDatabaseV3 this.pwDatabaseV4 = null diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.kt index f4e45e396..6b00782b7 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.kt @@ -209,7 +209,7 @@ class PwDatabaseV4 : PwDatabase { // Retrieve recycle bin in index val recycleBin: PwGroupV4? - get() = getGroupByUUID(recycleBinUUID) + get() = if (recycleBinUUID == UUID_ZERO) null else getGroupByUUID(recycleBinUUID) val lastSelectedGroup: PwGroupV4? get() = getGroupByUUID(lastSelectedGroupUUID) @@ -397,7 +397,7 @@ class PwDatabaseV4 : PwDatabase { * Ensure that the recycle bin tree exists, if enabled and create it * if it doesn't exist */ - private fun ensureRecycleBin(resources: Resources) { + fun ensureRecycleBinExists(resources: Resources) { if (recycleBin == null) { // Create recycle bin val recycleBinGroup = createGroup().apply { @@ -413,6 +413,13 @@ class PwDatabaseV4 : PwDatabase { } } + fun removeRecycleBin() { + if (recycleBin != null) { + recycleBinUUID = UUID_ZERO + recycleBinChanged = PwDate().date + } + } + /** * Define if a Node must be delete or recycle when remove action is called * @param node Node to remove @@ -422,21 +429,21 @@ class PwDatabaseV4 : PwDatabase { if (!isRecycleBinEnabled) return false if (recycleBin == null) - return true // TODO Create recycle bin + return false if (!node.isContainedIn(recycleBin!!)) return true return false } fun recycle(group: PwGroupV4, resources: Resources) { - ensureRecycleBin(resources) + ensureRecycleBinExists(resources) removeGroupFrom(group, group.parent) addGroupTo(group, recycleBin) group.afterAssignNewParent() } fun recycle(entry: PwEntryV4, resources: Resources) { - ensureRecycleBin(resources) + ensureRecycleBinExists(resources) removeEntryFrom(entry, entry.parent) addEntryTo(entry, recycleBin) entry.afterAssignNewParent() diff --git a/app/src/main/java/com/kunzisoft/keepass/notifications/DatabaseTaskNotificationService.kt b/app/src/main/java/com/kunzisoft/keepass/notifications/DatabaseTaskNotificationService.kt index b7e2ced07..fb8db5c0e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/notifications/DatabaseTaskNotificationService.kt +++ b/app/src/main/java/com/kunzisoft/keepass/notifications/DatabaseTaskNotificationService.kt @@ -108,7 +108,7 @@ class DatabaseTaskNotificationService : NotificationService(), ProgressTaskUpdat ACTION_DATABASE_SAVE_MEMORY_USAGE_TASK, ACTION_DATABASE_SAVE_PARALLELISM_TASK, ACTION_DATABASE_SAVE_ITERATIONS_TASK -> buildDatabaseSaveElementActionTask(intent) - else -> null + else -> buildDatabaseSave(intent) } actionRunnable?.let { actionRunnableNotNull -> @@ -412,6 +412,19 @@ class DatabaseTaskNotificationService : NotificationService(), ProgressTaskUpdat } } + /** + * Save database without parameter + */ + private fun buildDatabaseSave(intent: Intent): ActionRunnable? { + return if (intent.hasExtra(SAVE_DATABASE_KEY)) { + SaveDatabaseRunnable(this, + Database.getInstance(), + intent.getBooleanExtra(SAVE_DATABASE_KEY, false)) + } else { + null + } + } + private class ActionRunnableAsyncTask(private val progressTaskUpdater: ProgressTaskUpdater, private val onPreExecute: () -> Unit, private val onPostExecute: (result: ActionRunnable.Result) -> Unit) @@ -471,6 +484,7 @@ class DatabaseTaskNotificationService : NotificationService(), ProgressTaskUpdat const val ACTION_DATABASE_SAVE_MEMORY_USAGE_TASK = "ACTION_DATABASE_SAVE_MEMORY_USAGE_TASK" const val ACTION_DATABASE_SAVE_PARALLELISM_TASK = "ACTION_DATABASE_SAVE_PARALLELISM_TASK" const val ACTION_DATABASE_SAVE_ITERATIONS_TASK = "ACTION_DATABASE_SAVE_ITERATIONS_TASK" + const val ACTION_DATABASE_SAVE = "ACTION_DATABASE_SAVE" const val DATABASE_URI_KEY = "DATABASE_URI_KEY" const val MASTER_PASSWORD_CHECKED_KEY = "MASTER_PASSWORD_CHECKED_KEY" diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.kt b/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.kt index 091479a9d..90e04671b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.kt @@ -85,6 +85,7 @@ class NestedSettingsFragment : PreferenceFragmentCompat() { private var dbDefaultUsername: InputTextPreference? = null private var dbCustomColorPref: DialogColorPreference? = null private var dbDataCompressionPref: Preference? = null + private var recycleBinGroupPref: Preference? = null private var dbMaxHistoryItemsPref: InputNumberPreference? = null private var dbMaxHistorySizePref: InputNumberPreference? = null private var mEncryptionAlgorithmPref: DialogListExplanationPreference? = null @@ -452,17 +453,35 @@ class NestedSettingsFragment : PreferenceFragmentCompat() { } val dbRecycleBinPrefCategory: PreferenceCategory? = findPreference(getString(R.string.database_category_recycle_bin_key)) + recycleBinGroupPref = findPreference(getString(R.string.recycle_bin_group_key)) // Recycle bin - val recycleBinPref: SwitchPreference? = findPreference(getString(R.string.recycle_bin_key)) if (mDatabase.allowRecycleBin) { - recycleBinPref?.isChecked = mDatabase.isRecycleBinEnabled - // TODO Recycle Bin - recycleBinPref?.isEnabled = false + val recycleBinEnablePref: SwitchPreference? = findPreference(getString(R.string.recycle_bin_enable_key)) + recycleBinEnablePref?.apply { + isChecked = mDatabase.isRecycleBinEnabled + setOnPreferenceChangeListener { _, newValue -> + val recycleBinEnabled = newValue as Boolean + mDatabase.isRecycleBinEnabled = recycleBinEnabled + if (recycleBinEnabled) { + mDatabase.ensureRecycleBinExists(resources) + } else { + mDatabase.removeRecycleBin() + } + refreshRecycleBinGroup() + // Save the database + // TODO manual save + (context as SettingsActivity?)?.progressDialogThread?.startDatabaseSave(true) + true + } + } + // Recycle Bin group + refreshRecycleBinGroup() } else { dbRecycleBinPrefCategory?.isVisible = false } + // History findPreference(getString(R.string.database_category_history_key)) ?.isVisible = mDatabase.manageHistory == true @@ -481,6 +500,18 @@ class NestedSettingsFragment : PreferenceFragmentCompat() { } } + private fun refreshRecycleBinGroup() { + recycleBinGroupPref?.apply { + if (mDatabase.isRecycleBinEnabled) { + summary = mDatabase.recycleBin?.title + isEnabled = true + } else { + summary = null + isEnabled = false + } + } + } + private fun onCreateDatabaseSecurityPreference(rootKey: String?) { setPreferencesFromResource(R.xml.preferences_database_security, rootKey) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index c3f4f2b60..75aebbdbb 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -220,7 +220,7 @@ Chemin de fichier Afficher le chemin complet du fichier Utiliser la corbeille - Déplace les groupes et les entrées dans la « Corbeille » avant suppression + Déplace les groupes et les entrées dans le groupe \"Corbeille\" avant suppression Police de champ Changer la police utilisée dans les champs pour une meilleure visibilité des caractères Confiance dans le presse-papier diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml index f23e0e769..86ce22b2c 100644 --- a/app/src/main/res/values/donottranslate.xml +++ b/app/src/main/res/values/donottranslate.xml @@ -160,7 +160,8 @@ database_data_compression_key database_category_recycle_bin_key - recycle_bin_key + recycle_bin_enable_key + recycle_bin_group_key database_category_history_key max_history_items_key diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 001287855..c7a084fa0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -305,7 +305,8 @@ Data compression Data compression reduces the size of the database. Use recycle bin - Moves groups and entries to \"Recycle bin\" before deleting + Moves groups and entries to \"Recycle bin\" group before deleting + Recycle bin group Max. history items Limit number of history items per entry Max. history size diff --git a/app/src/main/res/xml/preferences_database.xml b/app/src/main/res/xml/preferences_database.xml index 42772b401..9dab9f174 100644 --- a/app/src/main/res/xml/preferences_database.xml +++ b/app/src/main/res/xml/preferences_database.xml @@ -74,11 +74,15 @@ android:title="@string/recycle_bin"> + From 477a78420186fa8c679cca4b6f49bdcf983fba40 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Thu, 14 Nov 2019 15:56:30 +0100 Subject: [PATCH 3/3] Empty recycle bin --- .../kunzisoft/keepass/activities/GroupActivity.kt | 13 +++++++++++++ app/src/main/res/menu/recycle_bin.xml | 10 ++++++++++ app/src/main/res/values/strings.xml | 1 + 3 files changed, 24 insertions(+) create mode 100644 app/src/main/res/menu/recycle_bin.xml diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt index 48ae8fbc5..44bd88a1e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt @@ -646,6 +646,12 @@ class GroupActivity : LockingActivity(), MenuUtil.contributionMenuInflater(inflater, menu) } + // Menu for recycle bin + if (mDatabase?.isRecycleBinEnabled == true + && mDatabase?.recycleBin == mCurrentGroup) { + inflater.inflate(R.menu.recycle_bin, menu) + } + // Get the SearchView and set the searchable configuration val searchManager = getSystemService(Context.SEARCH_SERVICE) as SearchManager @@ -754,6 +760,13 @@ class GroupActivity : LockingActivity(), lockAndExit() return true } + R.id.menu_empty_recycle_bin -> { + mCurrentGroup?.getChildren()?.let { listChildren -> + // Automatically delete all elements + onDeleteMenuClick(listChildren) + } + return true + } else -> { // Check the time lock before launching settings MenuUtil.onDefaultMenuOptionsItemSelected(this, item, mReadOnly, true) diff --git a/app/src/main/res/menu/recycle_bin.xml b/app/src/main/res/menu/recycle_bin.xml new file mode 100644 index 000000000..1b6cc50a1 --- /dev/null +++ b/app/src/main/res/menu/recycle_bin.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c7a084fa0..f54c8efec 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -183,6 +183,7 @@ Go to URL Write-protected Modifiable + Empty Recycle Bin Minus Never No search results