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.