mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Merge branch 'feature/Move_Group' into develop #658
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
KeePassDX(3.0.0)
|
||||
*
|
||||
* Move groups #658
|
||||
|
||||
KeePassDX(2.9.17)
|
||||
* Import / Export app properties #839
|
||||
|
||||
@@ -812,74 +812,75 @@ class GroupActivity : LockingActivity(),
|
||||
|
||||
override fun onPasteMenuClick(pasteMode: ListNodesFragment.PasteMode?,
|
||||
nodes: List<Node>): Boolean {
|
||||
// Move or copy only if allowed (in root if allowed)
|
||||
if (mCurrentGroup != mDatabase?.rootGroup
|
||||
|| mDatabase?.rootCanContainsEntry() == true) {
|
||||
|
||||
when (pasteMode) {
|
||||
ListNodesFragment.PasteMode.PASTE_FROM_COPY -> {
|
||||
// Copy
|
||||
mCurrentGroup?.let { newParent ->
|
||||
mProgressDatabaseTaskProvider?.startDatabaseCopyNodes(
|
||||
nodes,
|
||||
newParent,
|
||||
!mReadOnly && mAutoSaveEnable
|
||||
)
|
||||
}
|
||||
}
|
||||
ListNodesFragment.PasteMode.PASTE_FROM_MOVE -> {
|
||||
// Move
|
||||
mCurrentGroup?.let { newParent ->
|
||||
mProgressDatabaseTaskProvider?.startDatabaseMoveNodes(
|
||||
nodes,
|
||||
newParent,
|
||||
!mReadOnly && mAutoSaveEnable
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
when (pasteMode) {
|
||||
ListNodesFragment.PasteMode.PASTE_FROM_COPY -> {
|
||||
// Copy
|
||||
mCurrentGroup?.let { newParent ->
|
||||
mProgressDatabaseTaskProvider?.startDatabaseCopyNodes(
|
||||
nodes,
|
||||
newParent,
|
||||
!mReadOnly && mAutoSaveEnable
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
coordinatorLayout?.let { coordinatorLayout ->
|
||||
Snackbar.make(coordinatorLayout,
|
||||
R.string.error_copy_entry_here,
|
||||
Snackbar.LENGTH_LONG).asError().show()
|
||||
ListNodesFragment.PasteMode.PASTE_FROM_MOVE -> {
|
||||
// Move
|
||||
mCurrentGroup?.let { newParent ->
|
||||
mProgressDatabaseTaskProvider?.startDatabaseMoveNodes(
|
||||
nodes,
|
||||
newParent,
|
||||
!mReadOnly && mAutoSaveEnable
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
finishNodeAction()
|
||||
return true
|
||||
}
|
||||
|
||||
private fun eachNodeRecyclable(nodes: List<Node>): Boolean {
|
||||
mDatabase?.let { database ->
|
||||
return nodes.find { node ->
|
||||
var cannotRecycle = true
|
||||
if (node is Entry) {
|
||||
cannotRecycle = !database.canRecycle(node)
|
||||
} else if (node is Group) {
|
||||
cannotRecycle = !database.canRecycle(node)
|
||||
}
|
||||
cannotRecycle
|
||||
} == null
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun deleteNodes(nodes: List<Node>, recycleBin: Boolean = false): Boolean {
|
||||
val database = mDatabase
|
||||
mDatabase?.let { database ->
|
||||
|
||||
// If recycle bin enabled, ensure it exists
|
||||
if (database != null && database.isRecycleBinEnabled) {
|
||||
database.ensureRecycleBinExists(resources)
|
||||
}
|
||||
|
||||
// If recycle bin enabled and not in recycle bin, move in recycle bin
|
||||
if (database != null
|
||||
&& database.isRecycleBinEnabled
|
||||
&& database.recycleBin != mCurrentGroup) {
|
||||
|
||||
mProgressDatabaseTaskProvider?.startDatabaseDeleteNodes(
|
||||
nodes,
|
||||
!mReadOnly && mAutoSaveEnable
|
||||
)
|
||||
}
|
||||
// else open the dialog to confirm deletion
|
||||
else {
|
||||
val deleteNodesDialogFragment: DeleteNodesDialogFragment =
|
||||
if (recycleBin) {
|
||||
EmptyRecycleBinDialogFragment.getInstance(nodes)
|
||||
} else {
|
||||
DeleteNodesDialogFragment.getInstance(nodes)
|
||||
// If recycle bin enabled, ensure it exists
|
||||
if (database.isRecycleBinEnabled) {
|
||||
database.ensureRecycleBinExists(resources)
|
||||
}
|
||||
deleteNodesDialogFragment.show(supportFragmentManager, "deleteNodesDialogFragment")
|
||||
|
||||
// If recycle bin enabled and not in recycle bin, move in recycle bin
|
||||
if (eachNodeRecyclable(nodes)) {
|
||||
mProgressDatabaseTaskProvider?.startDatabaseDeleteNodes(
|
||||
nodes,
|
||||
!mReadOnly && mAutoSaveEnable
|
||||
)
|
||||
}
|
||||
// else open the dialog to confirm deletion
|
||||
else {
|
||||
val deleteNodesDialogFragment: DeleteNodesDialogFragment =
|
||||
if (recycleBin) {
|
||||
EmptyRecycleBinDialogFragment.getInstance(nodes)
|
||||
} else {
|
||||
DeleteNodesDialogFragment.getInstance(nodes)
|
||||
}
|
||||
deleteNodesDialogFragment.show(supportFragmentManager, "deleteNodesDialogFragment")
|
||||
}
|
||||
finishNodeAction()
|
||||
}
|
||||
finishNodeAction()
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -1076,6 +1077,16 @@ class GroupActivity : LockingActivity(),
|
||||
}
|
||||
}
|
||||
|
||||
override fun isValidGroupName(name: String): GroupEditDialogFragment.Error {
|
||||
if (name.isEmpty()) {
|
||||
return GroupEditDialogFragment.Error(true, R.string.error_no_name)
|
||||
}
|
||||
if (mDatabase?.groupNamesNotAllowed?.find { it.equals(name, ignoreCase = true) } != null) {
|
||||
return GroupEditDialogFragment.Error(true, R.string.error_word_reserved)
|
||||
}
|
||||
return GroupEditDialogFragment.Error(false, null)
|
||||
}
|
||||
|
||||
override fun approveEditGroup(action: GroupEditDialogFragment.EditGroupDialogAction,
|
||||
groupInfo: GroupInfo) {
|
||||
|
||||
|
||||
@@ -219,14 +219,19 @@ class GroupEditDialogFragment : DialogFragment() {
|
||||
}
|
||||
|
||||
private fun isValid(): Boolean {
|
||||
if (nameTextView.text.toString().isEmpty()) {
|
||||
nameTextLayoutView.error = getString(R.string.error_no_name)
|
||||
return false
|
||||
val error = mEditGroupListener?.isValidGroupName(nameTextView.text.toString()) ?: Error(false, null)
|
||||
error.messageId?.let { messageId ->
|
||||
nameTextLayoutView.error = getString(messageId)
|
||||
} ?: kotlin.run {
|
||||
nameTextLayoutView.error = null
|
||||
}
|
||||
return true
|
||||
return !error.isError
|
||||
}
|
||||
|
||||
data class Error(val isError: Boolean, val messageId: Int?)
|
||||
|
||||
interface EditGroupListener {
|
||||
fun isValidGroupName(name: String): Error
|
||||
fun approveEditGroup(action: EditGroupDialogAction,
|
||||
groupInfo: GroupInfo)
|
||||
fun cancelEditGroup(action: EditGroupDialogAction,
|
||||
|
||||
@@ -311,13 +311,17 @@ class ListNodesFragment : StylishFragment(), SortDialogFragment.SortSelectionLis
|
||||
menu?.removeItem(R.id.menu_edit)
|
||||
}
|
||||
|
||||
// Copy and Move (not for groups)
|
||||
// Move
|
||||
if (readOnly
|
||||
|| isASearchResult) {
|
||||
menu?.removeItem(R.id.menu_move)
|
||||
}
|
||||
|
||||
// Copy (not allowed for group)
|
||||
if (readOnly
|
||||
|| isASearchResult
|
||||
|| nodes.any { it.type == Type.GROUP }) {
|
||||
// TODO Copy For Group
|
||||
menu?.removeItem(R.id.menu_copy)
|
||||
menu?.removeItem(R.id.menu_move)
|
||||
}
|
||||
|
||||
// Deletion
|
||||
|
||||
@@ -24,7 +24,7 @@ import android.util.Log
|
||||
import com.kunzisoft.keepass.database.element.*
|
||||
import com.kunzisoft.keepass.database.element.node.Node
|
||||
import com.kunzisoft.keepass.database.element.node.Type
|
||||
import com.kunzisoft.keepass.database.exception.EntryDatabaseException
|
||||
import com.kunzisoft.keepass.database.exception.MoveEntryDatabaseException
|
||||
import com.kunzisoft.keepass.database.exception.MoveGroupDatabaseException
|
||||
|
||||
class MoveNodesRunnable constructor(
|
||||
@@ -47,8 +47,10 @@ class MoveNodesRunnable constructor(
|
||||
when (nodeToMove.type) {
|
||||
Type.GROUP -> {
|
||||
val groupToMove = nodeToMove as Group
|
||||
// Move group in new parent if not in the current group
|
||||
if (groupToMove != mNewParent
|
||||
// Move group if the parent change
|
||||
if (mOldParent != mNewParent
|
||||
// and if not in the current group
|
||||
&& groupToMove != mNewParent
|
||||
&& !mNewParent.isContainedIn(groupToMove)) {
|
||||
nodeToMove.touch(modified = true, touchParents = true)
|
||||
database.moveGroupTo(groupToMove, mNewParent)
|
||||
@@ -68,7 +70,7 @@ class MoveNodesRunnable constructor(
|
||||
database.moveEntryTo(entryToMove, mNewParent)
|
||||
} else {
|
||||
// Only finish thread
|
||||
setError(EntryDatabaseException())
|
||||
setError(MoveEntryDatabaseException())
|
||||
break@foreachNode
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import android.content.ContentResolver
|
||||
import android.content.res.Resources
|
||||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import com.kunzisoft.keepass.utils.readBytes4ToUInt
|
||||
import com.kunzisoft.keepass.database.action.node.NodeHandler
|
||||
import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
|
||||
import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine
|
||||
@@ -55,6 +54,7 @@ import com.kunzisoft.keepass.model.MainCredential
|
||||
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
|
||||
import com.kunzisoft.keepass.utils.SingletonHolder
|
||||
import com.kunzisoft.keepass.utils.UriUtil
|
||||
import com.kunzisoft.keepass.utils.readBytes4ToUInt
|
||||
import java.io.*
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
@@ -359,15 +359,10 @@ class Database {
|
||||
return null
|
||||
}
|
||||
|
||||
fun ensureRecycleBinExists(resources: Resources) {
|
||||
mDatabaseKDB?.ensureBackupExists()
|
||||
mDatabaseKDBX?.ensureRecycleBinExists(resources)
|
||||
}
|
||||
|
||||
fun removeRecycleBin() {
|
||||
// Don't allow remove backup in KDB
|
||||
mDatabaseKDBX?.removeRecycleBin()
|
||||
}
|
||||
val groupNamesNotAllowed: List<String>
|
||||
get() {
|
||||
return mDatabaseKDB?.groupNamesNotAllowed ?: ArrayList()
|
||||
}
|
||||
|
||||
private fun setDatabaseKDB(databaseKDB: DatabaseKDB) {
|
||||
this.mDatabaseKDB = databaseKDB
|
||||
@@ -790,11 +785,11 @@ class Database {
|
||||
}
|
||||
|
||||
fun addGroupTo(group: Group, parent: Group) {
|
||||
group.groupKDB?.let { entryKDB ->
|
||||
mDatabaseKDB?.addGroupTo(entryKDB, parent.groupKDB)
|
||||
group.groupKDB?.let { groupKDB ->
|
||||
mDatabaseKDB?.addGroupTo(groupKDB, parent.groupKDB)
|
||||
}
|
||||
group.groupKDBX?.let { entryKDBX ->
|
||||
mDatabaseKDBX?.addGroupTo(entryKDBX, parent.groupKDBX)
|
||||
group.groupKDBX?.let { groupKDBX ->
|
||||
mDatabaseKDBX?.addGroupTo(groupKDBX, parent.groupKDBX)
|
||||
}
|
||||
group.afterAssignNewParent()
|
||||
}
|
||||
@@ -809,11 +804,11 @@ class Database {
|
||||
}
|
||||
|
||||
fun removeGroupFrom(group: Group, parent: Group) {
|
||||
group.groupKDB?.let { entryKDB ->
|
||||
mDatabaseKDB?.removeGroupFrom(entryKDB, parent.groupKDB)
|
||||
group.groupKDB?.let { groupKDB ->
|
||||
mDatabaseKDB?.removeGroupFrom(groupKDB, parent.groupKDB)
|
||||
}
|
||||
group.groupKDBX?.let { entryKDBX ->
|
||||
mDatabaseKDBX?.removeGroupFrom(entryKDBX, parent.groupKDBX)
|
||||
group.groupKDBX?.let { groupKDBX ->
|
||||
mDatabaseKDBX?.removeGroupFrom(groupKDBX, parent.groupKDBX)
|
||||
}
|
||||
group.afterAssignNewParent()
|
||||
}
|
||||
@@ -888,6 +883,16 @@ class Database {
|
||||
}
|
||||
}
|
||||
|
||||
fun ensureRecycleBinExists(resources: Resources) {
|
||||
mDatabaseKDB?.ensureBackupExists()
|
||||
mDatabaseKDBX?.ensureRecycleBinExists(resources)
|
||||
}
|
||||
|
||||
fun removeRecycleBin() {
|
||||
// Don't allow remove backup in KDB
|
||||
mDatabaseKDBX?.removeRecycleBin()
|
||||
}
|
||||
|
||||
fun canRecycle(entry: Entry): Boolean {
|
||||
var canRecycle: Boolean? = null
|
||||
entry.entryKDB?.let {
|
||||
|
||||
@@ -368,14 +368,6 @@ class Group : Node, GroupVersionedInterface<Group, Entry> {
|
||||
groupKDB?.nodeId = id
|
||||
}
|
||||
|
||||
fun getLevel(): Int {
|
||||
return groupKDB?.level ?: -1
|
||||
}
|
||||
|
||||
fun setLevel(level: Int) {
|
||||
groupKDB?.level = level
|
||||
}
|
||||
|
||||
/*
|
||||
------------
|
||||
KDBX Methods
|
||||
|
||||
@@ -38,8 +38,6 @@ import kotlin.collections.ArrayList
|
||||
|
||||
class DatabaseKDB : DatabaseVersioned<Int, UUID, GroupKDB, EntryKDB>() {
|
||||
|
||||
private var backupGroupId: Int = BACKUP_FOLDER_UNDEFINED_ID
|
||||
|
||||
private var kdfListV3: MutableList<KdfEngine> = ArrayList()
|
||||
|
||||
override val version: String
|
||||
@@ -55,13 +53,14 @@ class DatabaseKDB : DatabaseVersioned<Int, UUID, GroupKDB, EntryKDB>() {
|
||||
return getGroupById(NodeIdInt(groupId))
|
||||
}
|
||||
|
||||
// Retrieve backup group in index
|
||||
val backupGroup: GroupKDB?
|
||||
get() {
|
||||
return if (backupGroupId == BACKUP_FOLDER_UNDEFINED_ID)
|
||||
null
|
||||
else
|
||||
getGroupById(backupGroupId)
|
||||
return retrieveBackup()
|
||||
}
|
||||
|
||||
val groupNamesNotAllowed: List<String>
|
||||
get() {
|
||||
return listOf(BACKUP_FOLDER_TITLE)
|
||||
}
|
||||
|
||||
override val kdfEngine: KdfEngine
|
||||
@@ -80,12 +79,7 @@ class DatabaseKDB : DatabaseVersioned<Int, UUID, GroupKDB, EntryKDB>() {
|
||||
|
||||
val rootGroups: List<GroupKDB>
|
||||
get() {
|
||||
val kids = ArrayList<GroupKDB>()
|
||||
doForEachGroupInIndex { group ->
|
||||
if (group.level == 0)
|
||||
kids.add(group)
|
||||
}
|
||||
return kids
|
||||
return rootGroup?.getChildGroups() ?: ArrayList()
|
||||
}
|
||||
|
||||
override val passwordEncoding: String
|
||||
@@ -169,21 +163,14 @@ class DatabaseKDB : DatabaseVersioned<Int, UUID, GroupKDB, EntryKDB>() {
|
||||
|
||||
override fun isInRecycleBin(group: GroupKDB): Boolean {
|
||||
var currentGroup: GroupKDB? = group
|
||||
val currentBackupGroup = backupGroup ?: return false
|
||||
|
||||
// Init backup group variable
|
||||
if (backupGroupId == BACKUP_FOLDER_UNDEFINED_ID)
|
||||
findBackupGroupId()
|
||||
|
||||
if (backupGroup == null)
|
||||
return false
|
||||
|
||||
if (currentGroup == backupGroup)
|
||||
if (currentGroup == currentBackupGroup)
|
||||
return true
|
||||
|
||||
val backupGroupId = currentBackupGroup.id
|
||||
while (currentGroup != null) {
|
||||
if (currentGroup.level == 0
|
||||
&& currentGroup.title.equals(BACKUP_FOLDER_TITLE, ignoreCase = true)) {
|
||||
backupGroupId = currentGroup.id
|
||||
if (backupGroupId == currentGroup.id) {
|
||||
return true
|
||||
}
|
||||
currentGroup = currentGroup.parent
|
||||
@@ -191,12 +178,12 @@ class DatabaseKDB : DatabaseVersioned<Int, UUID, GroupKDB, EntryKDB>() {
|
||||
return false
|
||||
}
|
||||
|
||||
private fun findBackupGroupId() {
|
||||
rootGroups.forEach { currentGroup ->
|
||||
if (currentGroup.level == 0
|
||||
&& currentGroup.title.equals(BACKUP_FOLDER_TITLE, ignoreCase = true)) {
|
||||
backupGroupId = currentGroup.id
|
||||
}
|
||||
/**
|
||||
* Retrieve backup group with his name
|
||||
*/
|
||||
private fun retrieveBackup(): GroupKDB? {
|
||||
return rootGroup?.searchChildGroup {
|
||||
it.title.equals(BACKUP_FOLDER_TITLE, ignoreCase = true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,8 +192,6 @@ class DatabaseKDB : DatabaseVersioned<Int, UUID, GroupKDB, EntryKDB>() {
|
||||
* if it doesn't exist
|
||||
*/
|
||||
fun ensureBackupExists() {
|
||||
findBackupGroupId()
|
||||
|
||||
if (backupGroup == null) {
|
||||
// Create recycle bin
|
||||
val recycleBinGroup = createGroup().apply {
|
||||
@@ -214,7 +199,6 @@ class DatabaseKDB : DatabaseVersioned<Int, UUID, GroupKDB, EntryKDB>() {
|
||||
icon.standard = getStandardIcon(IconImageStandard.TRASH_ID)
|
||||
}
|
||||
addGroupTo(recycleBinGroup, rootGroup)
|
||||
backupGroupId = recycleBinGroup.id
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,6 +252,5 @@ class DatabaseKDB : DatabaseVersioned<Int, UUID, GroupKDB, EntryKDB>() {
|
||||
val TYPE = DatabaseKDB::class.java
|
||||
|
||||
const val BACKUP_FOLDER_TITLE = "Backup"
|
||||
private const val BACKUP_FOLDER_UNDEFINED_ID = -1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,6 @@ import android.content.res.Resources
|
||||
import android.util.Base64
|
||||
import android.util.Log
|
||||
import com.kunzisoft.encrypt.HashManager
|
||||
import com.kunzisoft.keepass.utils.UnsignedInt
|
||||
import com.kunzisoft.keepass.utils.longTo8Bytes
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.database.action.node.NodeHandler
|
||||
import com.kunzisoft.keepass.database.crypto.AesEngine
|
||||
@@ -50,6 +48,8 @@ import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX.Companion.FILE_VER
|
||||
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX.Companion.FILE_VERSION_32_4
|
||||
import com.kunzisoft.keepass.utils.StringUtil.removeSpaceChars
|
||||
import com.kunzisoft.keepass.utils.StringUtil.toHexString
|
||||
import com.kunzisoft.keepass.utils.UnsignedInt
|
||||
import com.kunzisoft.keepass.utils.longTo8Bytes
|
||||
import org.apache.commons.codec.binary.Hex
|
||||
import org.w3c.dom.Node
|
||||
import java.io.IOException
|
||||
@@ -132,7 +132,6 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
|
||||
icon.standard = getStandardIcon(IconImageStandard.FOLDER_ID)
|
||||
}
|
||||
rootGroup = group
|
||||
addGroupIndex(group)
|
||||
}
|
||||
|
||||
override val version: String
|
||||
@@ -615,6 +614,9 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
|
||||
return false
|
||||
if (recycleBin == null)
|
||||
return false
|
||||
if (node is GroupKDBX
|
||||
&& recycleBin!!.isContainedIn(node))
|
||||
return false
|
||||
if (!node.isContainedIn(recycleBin!!))
|
||||
return true
|
||||
return false
|
||||
|
||||
@@ -35,7 +35,6 @@ import java.io.ByteArrayInputStream
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.io.UnsupportedEncodingException
|
||||
import java.security.MessageDigest
|
||||
import java.util.*
|
||||
|
||||
abstract class DatabaseVersioned<
|
||||
@@ -87,6 +86,12 @@ abstract class DatabaseVersioned<
|
||||
abstract val availableEncryptionAlgorithms: List<EncryptionAlgorithm>
|
||||
|
||||
var rootGroup: Group? = null
|
||||
set(value) {
|
||||
field = value
|
||||
value?.let {
|
||||
addGroupIndex(it)
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
protected abstract fun getMasterKey(key: String?, keyInputStream: InputStream?): ByteArray
|
||||
|
||||
@@ -31,14 +31,12 @@ import java.util.*
|
||||
|
||||
class GroupKDB : GroupVersioned<Int, UUID, GroupKDB, EntryKDB>, NodeKDBInterface {
|
||||
|
||||
var level = 0 // short
|
||||
// Used by KeePass internally, don't use
|
||||
var groupFlags = 0
|
||||
|
||||
constructor() : super()
|
||||
|
||||
constructor(parcel: Parcel) : super(parcel) {
|
||||
level = parcel.readInt()
|
||||
groupFlags = parcel.readInt()
|
||||
}
|
||||
|
||||
@@ -52,13 +50,11 @@ class GroupKDB : GroupVersioned<Int, UUID, GroupKDB, EntryKDB>, NodeKDBInterface
|
||||
|
||||
override fun writeToParcel(dest: Parcel, flags: Int) {
|
||||
super.writeToParcel(dest, flags)
|
||||
dest.writeInt(level)
|
||||
dest.writeInt(groupFlags)
|
||||
}
|
||||
|
||||
fun updateWith(source: GroupKDB) {
|
||||
super.updateWith(source)
|
||||
level = source.level
|
||||
groupFlags = source.groupFlags
|
||||
}
|
||||
|
||||
@@ -73,15 +69,12 @@ class GroupKDB : GroupVersioned<Int, UUID, GroupKDB, EntryKDB>, NodeKDBInterface
|
||||
return NodeIdInt(nodeId.id)
|
||||
}
|
||||
|
||||
override fun afterAssignNewParent() {
|
||||
if (parent != null)
|
||||
level = parent!!.level + 1
|
||||
}
|
||||
|
||||
fun setGroupId(groupId: Int) {
|
||||
this.nodeId = NodeIdInt(groupId)
|
||||
}
|
||||
|
||||
override fun afterAssignNewParent() {}
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmField
|
||||
|
||||
@@ -63,6 +63,17 @@ abstract class GroupVersioned
|
||||
get() = titleGroup
|
||||
set(value) { titleGroup = value }
|
||||
|
||||
/**
|
||||
* To determine the level from the root group (root group level is -1)
|
||||
*/
|
||||
fun getLevel(): Int {
|
||||
var level = -1
|
||||
parent?.let { parent ->
|
||||
level = parent.getLevel() + 1
|
||||
}
|
||||
return level
|
||||
}
|
||||
|
||||
override fun getChildGroups(): List<Group> {
|
||||
return childGroups
|
||||
}
|
||||
|
||||
@@ -45,23 +45,44 @@ interface GroupVersionedInterface<Group: GroupVersionedInterface<Group, Entry>,
|
||||
groupHandler.operate(this as Group)
|
||||
}
|
||||
|
||||
fun doForEachChild(entryHandler: NodeHandler<Entry>,
|
||||
fun doForEachChild(entryHandler: NodeHandler<Entry>?,
|
||||
groupHandler: NodeHandler<Group>?,
|
||||
stopIterationWhenGroupHandlerFails: Boolean = true): Boolean {
|
||||
for (entry in this.getChildEntries()) {
|
||||
if (!entryHandler.operate(entry))
|
||||
return false
|
||||
stopIterationWhenGroupHandlerOperateFalse: Boolean = true): Boolean {
|
||||
if (entryHandler != null) {
|
||||
for (entry in this.getChildEntries()) {
|
||||
if (!entryHandler.operate(entry))
|
||||
return false
|
||||
}
|
||||
}
|
||||
for (group in this.getChildGroups()) {
|
||||
var doActionForChild = true
|
||||
if (groupHandler != null && !groupHandler.operate(group)) {
|
||||
doActionForChild = false
|
||||
if (stopIterationWhenGroupHandlerFails)
|
||||
if (stopIterationWhenGroupHandlerOperateFalse)
|
||||
return false
|
||||
}
|
||||
if (doActionForChild)
|
||||
group.doForEachChild(entryHandler, groupHandler)
|
||||
group.doForEachChild(entryHandler, groupHandler, stopIterationWhenGroupHandlerOperateFalse)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun searchChildGroup(criteria: (group: Group) -> Boolean): Group? {
|
||||
return searchChildGroup(this, criteria)
|
||||
}
|
||||
|
||||
private fun searchChildGroup(rootGroup: GroupVersionedInterface<Group, Entry>,
|
||||
criteria: (group: Group) -> Boolean): Group? {
|
||||
for (childGroup in rootGroup.getChildGroups()) {
|
||||
if (criteria.invoke(childGroup)) {
|
||||
return childGroup
|
||||
} else {
|
||||
val subGroup = searchChildGroup(childGroup, criteria)
|
||||
if (subGroup != null) {
|
||||
return subGroup
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ class NoMemoryDatabaseException: LoadDatabaseException {
|
||||
constructor(exception: Throwable) : super(exception)
|
||||
}
|
||||
|
||||
class EntryDatabaseException: LoadDatabaseException {
|
||||
class MoveEntryDatabaseException: LoadDatabaseException {
|
||||
@StringRes
|
||||
override var errorId: Int = R.string.error_move_entry_here
|
||||
constructor() : super()
|
||||
@@ -123,7 +123,7 @@ class EntryDatabaseException: LoadDatabaseException {
|
||||
|
||||
class MoveGroupDatabaseException: LoadDatabaseException {
|
||||
@StringRes
|
||||
override var errorId: Int = R.string.error_move_folder_in_itself
|
||||
override var errorId: Int = R.string.error_move_group_here
|
||||
constructor() : super()
|
||||
constructor(exception: Throwable) : super(exception)
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ import java.security.MessageDigest
|
||||
import java.util.*
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.CipherInputStream
|
||||
import kotlin.collections.HashMap
|
||||
|
||||
|
||||
/**
|
||||
@@ -154,11 +155,10 @@ class DatabaseInputKDB(cacheDirectory: File,
|
||||
|
||||
// New manual root because KDB contains multiple root groups (here available with getRootGroups())
|
||||
val newRoot = mDatabase.createGroup()
|
||||
newRoot.level = -1
|
||||
mDatabase.rootGroup = newRoot
|
||||
mDatabase.addGroupIndex(newRoot)
|
||||
|
||||
// Import all nodes
|
||||
val groupLevelList = HashMap<GroupKDB, Int>()
|
||||
var newGroup: GroupKDB? = null
|
||||
var newEntry: EntryKDB? = null
|
||||
var currentGroupNumber = 0
|
||||
@@ -248,7 +248,7 @@ class DatabaseInputKDB(cacheDirectory: File,
|
||||
}
|
||||
0x0008 -> {
|
||||
newGroup?.let { group ->
|
||||
group.level = cipherInputStream.readBytes2ToUShort()
|
||||
groupLevelList.put(group, cipherInputStream.readBytes2ToUShort())
|
||||
} ?:
|
||||
newEntry?.let { entry ->
|
||||
entry.notes = cipherInputStream.readBytesToString(fieldSize)
|
||||
@@ -318,7 +318,7 @@ class DatabaseInputKDB(cacheDirectory: File,
|
||||
if (!Arrays.equals(messageDigest.digest(), header.contentsHash)) {
|
||||
throw InvalidCredentialsDatabaseException()
|
||||
}
|
||||
constructTreeFromIndex()
|
||||
constructTreeFromIndex(groupLevelList)
|
||||
|
||||
stopContentTimer()
|
||||
|
||||
@@ -339,34 +339,40 @@ class DatabaseInputKDB(cacheDirectory: File,
|
||||
return mDatabase
|
||||
}
|
||||
|
||||
private fun buildTreeGroups(previousGroup: GroupKDB, currentGroup: GroupKDB, groupIterator: Iterator<GroupKDB>) {
|
||||
private fun buildTreeGroups(groupLevelList: HashMap<GroupKDB, Int>,
|
||||
previousGroup: GroupKDB,
|
||||
currentGroup: GroupKDB,
|
||||
groupIterator: Iterator<GroupKDB>) {
|
||||
|
||||
if (currentGroup.parent == null && (previousGroup.level + 1) == currentGroup.level) {
|
||||
val previousGroupLevel = groupLevelList[previousGroup] ?: -1
|
||||
val currentGroupLevel = groupLevelList[currentGroup] ?: -1
|
||||
|
||||
if (currentGroup.parent == null && (previousGroupLevel + 1) == currentGroupLevel) {
|
||||
// Current group has an increment level compare to the previous, current group is a child
|
||||
previousGroup.addChildGroup(currentGroup)
|
||||
currentGroup.parent = previousGroup
|
||||
} else if (previousGroup.parent != null && previousGroup.level == currentGroup.level) {
|
||||
} else if (previousGroup.parent != null && previousGroupLevel == currentGroupLevel) {
|
||||
// In the same level, previous parent is the same as previous group
|
||||
previousGroup.parent!!.addChildGroup(currentGroup)
|
||||
currentGroup.parent = previousGroup.parent
|
||||
} else if (previousGroup.parent != null) {
|
||||
// Previous group has a higher level than the current group, check it's parent
|
||||
buildTreeGroups(previousGroup.parent!!, currentGroup, groupIterator)
|
||||
buildTreeGroups(groupLevelList, previousGroup.parent!!, currentGroup, groupIterator)
|
||||
}
|
||||
|
||||
// Next current group
|
||||
if (groupIterator.hasNext()){
|
||||
buildTreeGroups(currentGroup, groupIterator.next(), groupIterator)
|
||||
buildTreeGroups(groupLevelList, currentGroup, groupIterator.next(), groupIterator)
|
||||
}
|
||||
}
|
||||
|
||||
private fun constructTreeFromIndex() {
|
||||
mDatabase.rootGroup?.let {
|
||||
private fun constructTreeFromIndex(groupLevelList: HashMap<GroupKDB, Int>) {
|
||||
mDatabase.rootGroup?.let { root ->
|
||||
|
||||
// add each group
|
||||
val groupIterator = mDatabase.getGroupIndexes().iterator()
|
||||
if (groupIterator.hasNext())
|
||||
buildTreeGroups(it, groupIterator.next(), groupIterator)
|
||||
buildTreeGroups(groupLevelList, root, groupIterator.next(), groupIterator)
|
||||
|
||||
// add each child
|
||||
for (currentEntry in mDatabase.getEntryIndexes()) {
|
||||
|
||||
@@ -20,15 +20,15 @@
|
||||
package com.kunzisoft.keepass.database.file.output
|
||||
|
||||
import com.kunzisoft.encrypt.HashManager
|
||||
import com.kunzisoft.keepass.utils.UnsignedInt
|
||||
import com.kunzisoft.keepass.utils.write2BytesUShort
|
||||
import com.kunzisoft.keepass.utils.write4BytesUInt
|
||||
import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
|
||||
import com.kunzisoft.keepass.database.element.database.DatabaseKDB
|
||||
import com.kunzisoft.keepass.database.element.group.GroupKDB
|
||||
import com.kunzisoft.keepass.database.exception.DatabaseOutputException
|
||||
import com.kunzisoft.keepass.database.file.DatabaseHeader
|
||||
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDB
|
||||
import com.kunzisoft.keepass.utils.UnsignedInt
|
||||
import com.kunzisoft.keepass.utils.write2BytesUShort
|
||||
import com.kunzisoft.keepass.utils.write4BytesUInt
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.IOException
|
||||
@@ -59,6 +59,8 @@ class DatabaseOutputKDB(private val mDatabaseKDB: DatabaseKDB,
|
||||
override fun output() {
|
||||
// Before we output the header, we should sort our list of groups
|
||||
// and remove any orphaned nodes that are no longer part of the tree hierarchy
|
||||
// also remove the virtual root not present in kdb
|
||||
val rootGroup = mDatabaseKDB.rootGroup
|
||||
sortGroupsForOutput()
|
||||
|
||||
val header = outputHeader(mOutputStream)
|
||||
@@ -86,8 +88,10 @@ class DatabaseOutputKDB(private val mDatabaseKDB: DatabaseKDB,
|
||||
throw DatabaseOutputException("Invalid algorithm parameter.", e)
|
||||
} catch (e: IOException) {
|
||||
throw DatabaseOutputException("Failed to output final encrypted part.", e)
|
||||
} finally {
|
||||
// Add again the virtual root group for better management
|
||||
mDatabaseKDB.rootGroup = rootGroup
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Throws(DatabaseOutputException::class)
|
||||
@@ -201,7 +205,7 @@ class DatabaseOutputKDB(private val mDatabaseKDB: DatabaseKDB,
|
||||
|
||||
private fun sortGroupsForOutput() {
|
||||
val groupList = ArrayList<GroupKDB>()
|
||||
// Rebuild list according to coalation sorting order removing any orphaned groups
|
||||
// Rebuild list according to sorting order removing any orphaned groups
|
||||
for (rootGroup in mDatabaseKDB.rootGroups) {
|
||||
sortGroup(rootGroup, groupList)
|
||||
}
|
||||
|
||||
@@ -19,13 +19,9 @@
|
||||
*/
|
||||
package com.kunzisoft.keepass.database.file.output
|
||||
|
||||
import com.kunzisoft.keepass.utils.UnsignedInt
|
||||
import com.kunzisoft.keepass.utils.dateTo5Bytes
|
||||
import com.kunzisoft.keepass.utils.uIntTo4Bytes
|
||||
import com.kunzisoft.keepass.utils.uShortTo2Bytes
|
||||
import com.kunzisoft.keepass.utils.writeStringToStream
|
||||
import com.kunzisoft.keepass.database.element.group.GroupKDB
|
||||
import com.kunzisoft.keepass.database.exception.DatabaseOutputException
|
||||
import com.kunzisoft.keepass.utils.*
|
||||
import java.io.IOException
|
||||
import java.io.OutputStream
|
||||
|
||||
@@ -77,7 +73,7 @@ class GroupOutputKDB(private val mGroup: GroupKDB,
|
||||
// Level
|
||||
mOutputStream.write(LEVEL_FIELD_TYPE)
|
||||
mOutputStream.write(LEVEL_FIELD_SIZE)
|
||||
mOutputStream.write(uShortTo2Bytes(mGroup.level))
|
||||
mOutputStream.write(uShortTo2Bytes(mGroup.getLevel()))
|
||||
|
||||
// Flags
|
||||
mOutputStream.write(FLAGS_FIELD_TYPE)
|
||||
|
||||
@@ -137,7 +137,6 @@
|
||||
<string name="error_string_key">يجب أن يكون لكل سلسلة اسم حقل.</string>
|
||||
<string name="error_wrong_length">أدخل عددًا صحيحًا موجبًا في حقل «الطول».</string>
|
||||
<string name="error_autofill_enable_service">تعذر تمكين خدمة الملء التلقائي.</string>
|
||||
<string name="error_move_folder_in_itself">لا يمكن نقل مجموعة إلى نفسها.</string>
|
||||
<string name="file_not_found_content">تعذر إيجاد الملف. جرِّب فتحه من متصفح ملفات.</string>
|
||||
<string name="file_browser">مدير الملفات</string>
|
||||
<string name="invalid_credentials">تعذر قراءة الإعتمادات.</string>
|
||||
|
||||
@@ -85,7 +85,6 @@
|
||||
<string name="error_copy_group_here">Ovde ne možete kopirati grupu.</string>
|
||||
<string name="error_copy_entry_here">Ovde ne možete kopirati unos.</string>
|
||||
<string name="error_move_entry_here">Ovde ne možete premestiti unos.</string>
|
||||
<string name="error_move_folder_in_itself">Ne možete premestiti grupu u samu sebe.</string>
|
||||
<string name="error_autofill_enable_service">Nije moguće omogućiti uslugu automatskog popunjavanja.</string>
|
||||
<string name="error_wrong_length">Unesite pozitivan ceo broj u polje \"Dužina\".</string>
|
||||
<string name="error_label_exists">Ova oznaka već postoji.</string>
|
||||
|
||||
@@ -288,6 +288,5 @@
|
||||
<string name="error_string_type">Aquest text no coincideix amb l\'element sol·licitat.</string>
|
||||
<string name="error_otp_type">L\'OTP existent no està reconegut per aquest formulari, la seva validació ja no pot generar correctament el token.</string>
|
||||
<string name="error_create_database_file">No s\'ha pogut crear una base de dades amb aquesta contrasenya i arxiu de clau.</string>
|
||||
<string name="error_move_folder_in_itself">No pots moure un grup dintre d\'ell mateix.</string>
|
||||
<string name="error_autofill_enable_service">No s\'ha pogut habilitar el servei d\'autocompletat.</string>
|
||||
</resources>
|
||||
@@ -146,7 +146,6 @@
|
||||
<string name="error_load_database">Databázi se nepodařilo načíst.</string>
|
||||
<string name="error_load_database_KDF_memory">Klíč se nepodařilo načíst, zkuste snížit \"využití paměti\" pro KDF.</string>
|
||||
<string name="error_autofill_enable_service">Službu automatického vyplňování se nepodařilo zapnout.</string>
|
||||
<string name="error_move_folder_in_itself">Není možné přesunout skupinu do ní samotné.</string>
|
||||
<string name="file_not_found_content">Soubor nenalezen. Zkuste jej otevřít ze správce souborů.</string>
|
||||
<string name="list_entries_show_username_title">Zobrazit uživatelská jména</string>
|
||||
<string name="list_entries_show_username_summary">V seznamech záznamů zobrazit uživatelská jména</string>
|
||||
|
||||
@@ -145,7 +145,6 @@
|
||||
<string name="error_load_database">Databasen kunne ikke indlæses.</string>
|
||||
<string name="error_load_database_KDF_memory">Kunne ikke indlæse nøglen. Prøv at reducere KDF \"hukommelsesforbrug\".</string>
|
||||
<string name="error_autofill_enable_service">Kunne ikke aktivere autofyld tjenesten.</string>
|
||||
<string name="error_move_folder_in_itself">Kan ikke flytte en gruppe til sig selv.</string>
|
||||
<string name="file_not_found_content">Kunne ikke finde filen. Prøv at åbne den fra filhåndtering.</string>
|
||||
<string name="list_entries_show_username_title">Vis brugernavne</string>
|
||||
<string name="list_entries_show_username_summary">Vis brugernavne i postlister</string>
|
||||
|
||||
@@ -268,7 +268,6 @@
|
||||
<string name="contribute">Unterstützen</string>
|
||||
<string name="icon_pack_choose_title">Symbolpaket</string>
|
||||
<string name="icon_pack_choose_summary">In der App verwendetes Symbolpaket</string>
|
||||
<string name="error_move_folder_in_itself">Eine Gruppe kann nicht in sich selbst verschoben werden.</string>
|
||||
<string name="menu_copy">Kopieren</string>
|
||||
<string name="menu_move">Verschieben</string>
|
||||
<string name="menu_paste">Einfügen</string>
|
||||
|
||||
@@ -251,7 +251,6 @@
|
||||
<string name="style_choose_summary">Θέμα που χρησιμοποιείται στην εφαρμογή</string>
|
||||
<string name="icon_pack_choose_title">Πακέτο Εικονιδίων</string>
|
||||
<string name="icon_pack_choose_summary">Πακέτο εικονιδίων που χρησιμοποιείται στην εφαρμογή</string>
|
||||
<string name="error_move_folder_in_itself">Δεν μπορείτε να μετακινήσετε μια ομάδα μέσα στον εαυτό της.</string>
|
||||
<string name="menu_copy">Αντιγραφή</string>
|
||||
<string name="menu_move">Μετακίνηση</string>
|
||||
<string name="menu_paste">Επικόλληση</string>
|
||||
|
||||
@@ -267,7 +267,6 @@
|
||||
<string name="edit_entry">Editar entrada</string>
|
||||
<string name="error_load_database">No se pudo cargar la base de datos.</string>
|
||||
<string name="error_load_database_KDF_memory">No se pudo cargar la clave. Intente disminuir el uso de memoria de KDF.</string>
|
||||
<string name="error_move_folder_in_itself">No puede mover un grupo dentro de sí mismo.</string>
|
||||
<string name="list_entries_show_username_title">Enseña nombres de usuario</string>
|
||||
<string name="list_entries_show_username_summary">Enseña nombres de usuador en las listras de entradas</string>
|
||||
<string name="menu_copy">Copiar</string>
|
||||
|
||||
@@ -141,7 +141,6 @@
|
||||
<string name="error_copy_group_here">شما نمی توانید یک گروه را در اینجا کپی کنید.</string>
|
||||
<string name="error_copy_entry_here">شما نمی توانید یک ورودی را در اینجا کپی کنید.</string>
|
||||
<string name="error_move_entry_here">شما نمی توانید یک ورودی را به اینجا منتقل کنید.</string>
|
||||
<string name="error_move_folder_in_itself">شما نمی توانید یک گروه را به خود منتقل کنید.</string>
|
||||
<string name="error_autofill_enable_service">قادر به فعال کردن سرویس پر کردن خودکار نبود.</string>
|
||||
<string name="error_wrong_length">یک عدد کامل مثبت را در زمینه \"طول\" وارد کنید.</string>
|
||||
<string name="error_label_exists">این برچسب در حال حاضر وجود دارد.</string>
|
||||
|
||||
@@ -308,7 +308,6 @@
|
||||
<string name="error_copy_group_here">Et voi kopioida ryhmää tänne.</string>
|
||||
<string name="error_copy_entry_here">Et voi kopioida tietuetta tänne.</string>
|
||||
<string name="error_move_entry_here">Et voi siirtää tietuetta tänne.</string>
|
||||
<string name="error_move_folder_in_itself">Et voi siirtää ryhmää itsensä sisälle.</string>
|
||||
<string name="error_autofill_enable_service">Automaattista täyttöä ei voitu ottaa käyttöön.</string>
|
||||
<string name="content_description_node_children">Solmun lapset</string>
|
||||
</resources>
|
||||
@@ -275,7 +275,6 @@
|
||||
</string-array>
|
||||
<string name="icon_pack_choose_title">Collection d’icônes</string>
|
||||
<string name="icon_pack_choose_summary">Collection d’icônes utilisées dans l’application</string>
|
||||
<string name="error_move_folder_in_itself">Vous ne pouvez pas déplacer un groupe dans lui-même.</string>
|
||||
<string name="menu_copy">Copier</string>
|
||||
<string name="menu_move">Déplacer</string>
|
||||
<string name="menu_paste">Coller</string>
|
||||
|
||||
@@ -242,7 +242,6 @@
|
||||
<string name="error_disallow_no_credentials">Barem jedan skup podataka za prijavu mora biti postavljen.</string>
|
||||
<string name="error_string_key">Svaki niz mora imati ime polja.</string>
|
||||
<string name="error_autofill_enable_service">Nije moguće aktivirati uslugu automatskog ispunjavanja.</string>
|
||||
<string name="error_move_folder_in_itself">Nije moguće premjestiti grupu u samu sebe.</string>
|
||||
<string name="error_move_entry_here">Unos se ne može ovdje premijestiti.</string>
|
||||
<string name="error_copy_entry_here">Unos se ne može ovdje kopirati.</string>
|
||||
<string name="error_copy_group_here">Grupa se ne može ovjde kopirati.</string>
|
||||
|
||||
@@ -154,7 +154,6 @@
|
||||
<string name="error_load_database">Az adatbázis betöltése meghiúsult.</string>
|
||||
<string name="error_load_database_KDF_memory">A kulcs nem tölthető be. Próbálja meg csökkenteni a KDF „Memóriahasználatot”.</string>
|
||||
<string name="error_autofill_enable_service">Az automatikus kitöltési szolgáltatás nem engedélyezhető.</string>
|
||||
<string name="error_move_folder_in_itself">Nem helyezheti át a csoportot saját magába.</string>
|
||||
<string name="list_entries_show_username_title">Felhasználónevek megjelenítése</string>
|
||||
<string name="list_entries_show_username_summary">Felhasználónevek megjelenítése a bejegyzéslistákban</string>
|
||||
<string name="copy_field">%1$s másolata</string>
|
||||
|
||||
@@ -72,7 +72,6 @@
|
||||
<string name="error_copy_group_here">Anda tidak bisa menyalin grup di sini.</string>
|
||||
<string name="error_copy_entry_here">Anda tidak bisa menyalin entri di sini.</string>
|
||||
<string name="error_move_entry_here">Anda tidak bisa memindahkan entri ke sini.</string>
|
||||
<string name="error_move_folder_in_itself">Anda tidak bisa memindahkan grup ke grup itu sendiri.</string>
|
||||
<string name="error_autofill_enable_service">Tidak bisa mengaktifkan layanan IsiOtomatis.</string>
|
||||
<string name="error_wrong_length">Masukkan bilangan bulat di bidang \"Panjang\".</string>
|
||||
<string name="error_label_exists">Label ini sudah ada.</string>
|
||||
|
||||
@@ -154,7 +154,6 @@
|
||||
<string name="extended_ASCII">ASCII esteso</string>
|
||||
<string name="error_nokeyfile">Seleziona un file chiave.</string>
|
||||
<string name="error_autofill_enable_service">Attivazione del servizio di auto-completamento fallita.</string>
|
||||
<string name="error_move_folder_in_itself">Non puoi spostare un gruppo in se stesso.</string>
|
||||
<string name="menu_form_filling_settings">Riempimento campi</string>
|
||||
<string name="menu_copy">Copia</string>
|
||||
<string name="menu_move">Sposta</string>
|
||||
|
||||
@@ -120,7 +120,6 @@
|
||||
<string name="error_label_exists">このラベルはすでに存在します。</string>
|
||||
<string name="error_wrong_length">[長さ] フィールドには正の整数を入力してください。</string>
|
||||
<string name="error_autofill_enable_service">自動入力サービスを有効にできませんでした。</string>
|
||||
<string name="error_move_folder_in_itself">グループを自分自身の中に移動することはできません。</string>
|
||||
<string name="error_move_entry_here">ここではエントリーを移動することはできません。</string>
|
||||
<string name="error_copy_entry_here">ここではエントリーをコピーすることはできません。</string>
|
||||
<string name="error_copy_group_here">ここではグループをコピーすることはできません。</string>
|
||||
|
||||
@@ -78,7 +78,6 @@
|
||||
<string name="error_string_key">각 항목은 필드 이름을 가져야 합니다.</string>
|
||||
<string name="error_wrong_length">\"길이\" 필드에는 양수를 입력하십시오.</string>
|
||||
<string name="error_autofill_enable_service">자동 채우기 서비스를 활성화할 수 없습니다.</string>
|
||||
<string name="error_move_folder_in_itself">그룹을 자신에게 옮길 수 없습니다.</string>
|
||||
<string name="field_name">필드 이름</string>
|
||||
<string name="field_value">필드 값</string>
|
||||
<string name="file_not_found_content">파일을 찾을 수 없습니다. 파일 탐색기에서 열리는지 확인해 주세요.</string>
|
||||
|
||||
@@ -128,7 +128,6 @@
|
||||
<string name="error_create_database_file">ഈ പാസ്വേഡും കീഫയലും ഉപയോഗിച്ച് ഡാറ്റാബേസ് സൃഷ്ടിക്കാൻ കഴിയില്ല.</string>
|
||||
<string name="error_copy_group_here">നിങ്ങൾക്ക് ഇവിടെ ഒരു ഗ്രൂപ്പ് പകർത്താൻ കഴിയില്ല.</string>
|
||||
<string name="error_copy_entry_here">നിങ്ങൾക്ക് ഇവിടെ ഒരു എൻട്രി പകർത്താൻ കഴിയില്ല.</string>
|
||||
<string name="error_move_folder_in_itself">നിങ്ങൾക്ക് ഒരു ഗ്രൂപ്പിനെ അതിലേക്ക് നീക്കാൻ കഴിയില്ല.</string>
|
||||
<string name="error_label_exists">ഈ ലേബൽ ഇതിനകം നിലവിലുണ്ട്.</string>
|
||||
<string name="error_string_key">ഓരോ സ്ട്രിംഗിനും ഒരു ഫീൽഡ് നാമം ഉണ്ടായിരിക്കണം.</string>
|
||||
<string name="error_rounds_too_large">\"Transformation rounds\" too high. Setting to 2147483648.</string>
|
||||
|
||||
@@ -78,7 +78,6 @@
|
||||
<string name="error_string_key">Hver streng må ha et feltnavn.</string>
|
||||
<string name="error_wrong_length">Skriv inn et positivt heltall i \"Lengde\" feltet.</string>
|
||||
<string name="error_autofill_enable_service">Autofyll-tjenesten kan ikke skrus på.</string>
|
||||
<string name="error_move_folder_in_itself">Kan ikke flytte gruppe inn i seg selv.</string>
|
||||
<string name="field_name">Feltnavn</string>
|
||||
<string name="field_value">Feltverdi</string>
|
||||
<string name="file_not_found_content">Fant ikke filen. Prøv å åpne den fra din innholdsleverandør.</string>
|
||||
|
||||
@@ -143,7 +143,6 @@
|
||||
<string name="error_load_database_KDF_memory">De sleutel kan niet worden geladen. Probeer om het \"geheugengebruik\" van KDF te verminderen.</string>
|
||||
<string name="error_string_key">Elke zin moet een veldnaam bevatten.</string>
|
||||
<string name="error_autofill_enable_service">De dienst automatisch aanvullen kan niet worden ingeschakeld.</string>
|
||||
<string name="error_move_folder_in_itself">Een groep kan niet naar zichzelf worden verplaatst.</string>
|
||||
<string name="field_name">Veldnaam</string>
|
||||
<string name="field_value">Veldwaarde</string>
|
||||
<string name="file_not_found_content">Bestand niet gevonden. Probeer opnieuw te openen via bestandsbeheer.</string>
|
||||
|
||||
@@ -244,7 +244,6 @@
|
||||
<string name="error_create_database">ਡਾਟਾਬੇਸ ਫਾਈਲ ਬਣਾਉਣ ਲਈ ਅਸਮਰੱਥ।</string>
|
||||
<string name="error_copy_group_here">ਤੁਸੀਂ ਗਰੁੱਪ ਨੂੰ ਇੱਥੇ ਕਾਪੀ ਨਹੀਂ ਕਰ ਸਕਦੇ ਹੋ।</string>
|
||||
<string name="error_copy_entry_here">ਤੁਸੀੰ ਇਸ ਐੰਟਰੀ ਨੂੰ ਇੱਥੇ ਕਾਪੀ ਨਹੀਂ ਕਰ ਸਕਦੇ ਹੋ।</string>
|
||||
<string name="error_move_folder_in_itself">ਤੁਸੀੰ ਗਰੁੱਪ ਨੂੰ ਖੁਦ ਵਿੱਚ ਨਹੀਂ ਭੇਜ ਸਕਦੇ ਹੋ।</string>
|
||||
<string name="list_password_generator_options_title">ਪਾਸਵਰਡ ਅੱਖਰ</string>
|
||||
<string name="password_size_summary">ਤਿਆਰ ਕੀਤੇ ਪਾਸਵਰਡਾਂ ਲਈ ਮੂਲ ਆਕਾਰ ਸੈੱਟ ਕਰਦਾ ਹੈ</string>
|
||||
<string name="password_size_title">ਤਿਆਰ ਕੀਤੇ ਪਾਸਵਰਡ ਦਾ ਆਕਾਰ</string>
|
||||
|
||||
@@ -140,7 +140,6 @@
|
||||
<string name="error_load_database_KDF_memory">Nie można załadować klucza. Spróbuj zmniejszyć użycie pamięć KDF.</string>
|
||||
<string name="error_string_key">Każdy ciąg musi mieć nazwę pola.</string>
|
||||
<string name="error_autofill_enable_service">Nie można włączyć usługi autouzupełniania.</string>
|
||||
<string name="error_move_folder_in_itself">Nie można przenieść grupy do samej siebie.</string>
|
||||
<string name="field_name">Nazwa pola</string>
|
||||
<string name="field_value">Wartość pola</string>
|
||||
<string name="file_not_found_content">Nie znaleziono pliku. Spróbuj ponownie otworzyć go w przeglądarce plików.</string>
|
||||
|
||||
@@ -138,7 +138,6 @@
|
||||
<string name="entry_not_found">Não pôde encontrar dado de entrada.</string>
|
||||
<string name="error_string_key">Um nome do campo é necessário para cada string.</string>
|
||||
<string name="error_autofill_enable_service">Não pôde ser habilitado o serviço de preenchimento automático.</string>
|
||||
<string name="error_move_folder_in_itself">Você não pode mover um grupo para dentro de si mesmo.</string>
|
||||
<string name="field_name">Nome do campo</string>
|
||||
<string name="field_value">Valor do campo</string>
|
||||
<string name="file_not_found_content">Arquivo não encontrado. Tente reabri-lo de seu buscador de arquivos.</string>
|
||||
|
||||
@@ -148,7 +148,6 @@
|
||||
<string name="allow">Permitir</string>
|
||||
<string name="error_load_database">Não foi possível abrir a sua base de dados.</string>
|
||||
<string name="error_load_database_KDF_memory">Não foi possível carregar a chave. Tente descarregar o \"Uso de Memória\" do KDF.</string>
|
||||
<string name="error_move_folder_in_itself">Não pode mover um grupo para si mesmo.</string>
|
||||
<string name="list_entries_show_username_title">Mostrar nomes de utilizador</string>
|
||||
<string name="copy_field">Cópia de %1$s</string>
|
||||
<string name="menu_form_filling_settings">Preenchimento de formulário</string>
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
<string name="menu_paste">Colar</string>
|
||||
<string name="menu_move">Mover</string>
|
||||
<string name="menu_copy">Copiar</string>
|
||||
<string name="error_move_folder_in_itself">Não pode mover um grupo para si mesmo.</string>
|
||||
<string name="clipboard_cleared">Área de transferência limpa</string>
|
||||
<string name="about_description">Uma implementação do gestor de palavras-chave KeePass para Android</string>
|
||||
<string name="icon_pack_choose_summary">Pacote de ícones usado na app</string>
|
||||
|
||||
@@ -111,7 +111,6 @@
|
||||
<string name="error_string_key">Fiecare șir trebuie să aibă un nume de câmp.</string>
|
||||
<string name="error_wrong_length">Introduceți un număr întreg pozitiv în câmpul \"Lungime\".</string>
|
||||
<string name="error_autofill_enable_service">Nu s-a putut activa serviciul de completare automată.</string>
|
||||
<string name="error_move_folder_in_itself">Nu puteți muta un grup în sine.</string>
|
||||
<string name="error_move_entry_here">Nu puteți muta o intrare aici.</string>
|
||||
<string name="error_copy_entry_here">Nu puteți copia o intrare aici.</string>
|
||||
<string name="error_copy_group_here">Nu puteți copia un grup aici.</string>
|
||||
|
||||
@@ -168,7 +168,6 @@
|
||||
<string name="allow">Разрешить</string>
|
||||
<string name="error_load_database">Невозможно загрузить базу.</string>
|
||||
<string name="error_load_database_KDF_memory">Невозможно загрузить ключ. Попробуйте уменьшить размер памяти, используемой функцией формирования ключа (KDF).</string>
|
||||
<string name="error_move_folder_in_itself">Нельзя переместить группу в саму себя.</string>
|
||||
<string name="list_entries_show_username_title">Показывать имя</string>
|
||||
<string name="list_entries_show_username_summary">Показывать имя пользователя в списке записей</string>
|
||||
<string name="menu_copy">Копировать</string>
|
||||
|
||||
@@ -270,7 +270,6 @@
|
||||
<string name="style_choose_summary">Tema som används i appen</string>
|
||||
<string name="icon_pack_choose_title">Ikonpaket</string>
|
||||
<string name="icon_pack_choose_summary">Ikonpaket som används i appen</string>
|
||||
<string name="error_move_folder_in_itself">Du kan inte lägga en grupp i sig själv.</string>
|
||||
<string name="menu_copy">Kopia</string>
|
||||
<string name="menu_move">Flytta</string>
|
||||
<string name="menu_paste">Klistra in</string>
|
||||
|
||||
@@ -78,7 +78,6 @@
|
||||
<string name="error_string_key">Her dizginin bir alan adı olmalıdır.</string>
|
||||
<string name="error_wrong_length">\"Uzunluk\" alanına pozitif bir tam sayı girin.</string>
|
||||
<string name="error_autofill_enable_service">Otomatik doldurma hizmeti etkinleştirilemedi.</string>
|
||||
<string name="error_move_folder_in_itself">Bir grubu kendine taşıyamazsın.</string>
|
||||
<string name="field_name">Alan adı</string>
|
||||
<string name="field_value">Alan değeri</string>
|
||||
<string name="file_not_found_content">Dosya bulunamadı. Dosya tarayıcınızda yeniden açmayı deneyin.</string>
|
||||
|
||||
@@ -217,7 +217,6 @@
|
||||
<string name="error_copy_group_here">Ви не можете копіювати групу сюди.</string>
|
||||
<string name="error_copy_entry_here">Ви не можете копіювати записи сюди.</string>
|
||||
<string name="error_move_entry_here">Ви не можете перемістити запис сюди.</string>
|
||||
<string name="error_move_folder_in_itself">Ви не можете перемістити групу в себе саму.</string>
|
||||
<string name="error_autofill_enable_service">Не вдалось ввімкнути службу автозаповнення.</string>
|
||||
<string name="error_label_exists">Ця мітка вже існує.</string>
|
||||
<string name="error_string_key">Кожен рядок повинен мати назву поля.</string>
|
||||
|
||||
@@ -139,7 +139,6 @@
|
||||
<string name="error_load_database">无法加载数据库。</string>
|
||||
<string name="error_load_database_KDF_memory">无法加载密钥。尝试降低KDF的“内存使用”值。</string>
|
||||
<string name="error_autofill_enable_service">无法启用自动填充服务。</string>
|
||||
<string name="error_move_folder_in_itself">无法将群组移至它自身之中。</string>
|
||||
<string name="file_not_found_content">找不到文件。请重新打开文件。</string>
|
||||
<string name="invalid_algorithm">算法无效。</string>
|
||||
<string name="keyfile_is_empty">密钥文件为空。</string>
|
||||
|
||||
@@ -128,7 +128,6 @@
|
||||
<string name="clipboard_error">部份設備不容許其他程式使用剪貼簿。</string>
|
||||
<string name="clipboard_error_clear">無法清除剪貼簿</string>
|
||||
<string name="error_autofill_enable_service">無法啟用自動填入服務。</string>
|
||||
<string name="error_move_folder_in_itself">無法移動一個群組至自己本身。</string>
|
||||
<string name="invalid_algorithm">無效的演算法。</string>
|
||||
<string name="keyfile_is_empty">金鑰檔案是空白的。</string>
|
||||
<string name="menu_form_filling_settings">表格填入</string>
|
||||
|
||||
@@ -109,6 +109,7 @@
|
||||
<string name="error_invalid_path">Make sure the path is correct.</string>
|
||||
<string name="error_invalid_OTP">Invalid OTP secret.</string>
|
||||
<string name="error_no_name">Enter a name.</string>
|
||||
<string name="error_word_reserved">This word is reserved and cannot be used.</string>
|
||||
<string name="error_nokeyfile">Select a keyfile.</string>
|
||||
<string name="error_out_of_memory">No memory to load your entire database.</string>
|
||||
<string name="error_load_database">Could not load your database.</string>
|
||||
@@ -121,7 +122,7 @@
|
||||
<string name="error_label_exists">This label already exists.</string>
|
||||
<string name="error_wrong_length">Enter a positive integer number in the \"Length\" field.</string>
|
||||
<string name="error_autofill_enable_service">Could not enable autofill service.</string>
|
||||
<string name="error_move_folder_in_itself">You can not move a group into itself.</string>
|
||||
<string name="error_move_group_here">You can not move a group here.</string>
|
||||
<string name="error_move_entry_here">You can not move an entry here.</string>
|
||||
<string name="error_copy_entry_here">You can not copy an entry here.</string>
|
||||
<string name="error_copy_group_here">You cannot copy a group here.</string>
|
||||
|
||||
@@ -1 +1 @@
|
||||
*
|
||||
* Move groups #658
|
||||
@@ -1 +1 @@
|
||||
*
|
||||
* Déplacement de groupes #658
|
||||
Reference in New Issue
Block a user