Merge branch 'feature/Move_Group' into develop #658

This commit is contained in:
J-Jamet
2021-04-19 13:27:02 +02:00
52 changed files with 217 additions and 208 deletions

View File

@@ -1,5 +1,5 @@
KeePassDX(3.0.0)
*
* Move groups #658
KeePassDX(2.9.17)
* Import / Export app properties #839

View File

@@ -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) {

View File

@@ -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,

View File

@@ -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

View File

@@ -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
}
}

View File

@@ -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 {

View File

@@ -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

View File

@@ -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
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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
}

View File

@@ -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
}
}

View File

@@ -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)
}

View File

@@ -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()) {

View File

@@ -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)
}

View File

@@ -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)

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -275,7 +275,6 @@
</string-array>
<string name="icon_pack_choose_title">Collection dicônes</string>
<string name="icon_pack_choose_summary">Collection dicônes utilisées dans lapplication</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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -1 +1 @@
*
* Move groups #658

View File

@@ -1 +1 @@
*
* Déplacement de groupes #658