From 28e26002712e6af3ec2659696af9f7cc904d72ab Mon Sep 17 00:00:00 2001 From: Wilker Santana da Silva Date: Wed, 14 Aug 2019 18:50:01 +0000 Subject: [PATCH 01/43] Translated using Weblate (Portuguese (Brazil)) Currently translated at 99.4% (354 of 356 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pt_BR/ --- app/src/main/res/values-pt-rBR/strings.xml | 25 ++++++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index d1d8f5d52..af3094205 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -116,7 +116,7 @@ Instale um navegador para abrir esta URL. Bancos de dados recentes Não procurar por entradas no backup ou na lixeira - Omite o grupo \"Backup\" dos resultados da busca (apenas se aplica a arquivos .kdb) + Omite os grupos \"Backup\" e \"Lixeira\" dos resultados da busca Criando novo banco de dados… Trabalhando… Lembra o local dos arquivos-chave dos bancos de dados @@ -178,7 +178,7 @@ Algoritmo errado. Não existem arquivos-chave. O arquivo-chave está vazio. - Copiado %1$s + Cópia de %1$s Preenchimento de formulário Cópia Mover @@ -212,7 +212,7 @@ Use o framework de acesso de armazenamento (SAF) do Android para navegação de arquivos (KitKat e posterior) Framework de acesso de armazenamento Aviso - Evite caracteres fora da tabela Latin-1 em arquivos .kdb, pois todos estes são convertidos para a mesma letra. + Evite caracteres fora do formato de codificação do arquivo do banco (todos os caracteres não reconhecidos são convertidos para a mesma letra). Você realmente não quer proteção por senha\? Você tem certeza de que não quer usar uma chave de encriptação\? Impressão digital é suportada, mas não está configurada. @@ -236,7 +236,7 @@ Caracteres da senha Definir os caracteres padrão do gerador de senha Notificações da área de transferência - Habilite notificações da área de transferência para copiar campos de entrada + Habilite notificações da área de transferência para campos copiáveis quando estiver visualizando uma entrada Se a limpeza da área de transferência falhar, limpe seu histórico manualmente. Bloquear Bloqueio de tela @@ -325,7 +325,7 @@ Modifique a entrada Edite a sua entrada com campos personalizados. Os conjuntos de dados podem ser referenciados entre campos de entradas diferentes. Crie uma senha forte para sua entrada. - Gere uma senha forte para associar a sua entrada, defina-a facilmente de acordo com os critérios do formulário e não se esqueça de tornar a senha segura. + Gere uma senha forte para associar a sua entrada, defina-a facilmente de acordo com os critérios do formulário e não se esqueça de torná-la segura. Adicione campos customizados Registre facilmente um campo básico não fornecido que você também pode proteger. Desbloqueie seu banco de dados @@ -354,7 +354,7 @@ Esse recurso está em desenvolvimento e exige que sua contribuição para que esteja disponível em breve. Ao comprar a versão pro, - contribuindo , + Contribuindo , Você está incentivando os desenvolvedores a criar novos recursos e a corrigir erros de acordo com suas observações. Obrigado por sua contribuição. Estamos trabalhando duro para lançar esse recurso o mais rápido possível. @@ -388,10 +388,21 @@ %1$s disponível no Magikeyboard %1$s Limpar ao fechar - Limpar a entrada do teclado ao fechar a notificação + Fecha a base de dados ao dispensar a notificação Aparência Tema do teclado Teclas Vibrar ao clicar Som ao clicar + Modo de seleção + Não feche o aplicativo… + Trancar ao voltar + Tranca a base de dados quando o usuário pressiona o botão Voltar na tela inicial + Limpar a Área de Transferência ao fechar + Fecha a base de dados ao dispensar a notificação + Lixeira + Seleção de entrada + Mostrar campos de entrada no Magikeyboard quando estiver visualizando uma Entrada + Deletar senha + Deleta a senha inserida após uma tentativa de conexão \ No newline at end of file From 0b4dd1e9090f4f2d22efd040f49e4dfafb7b3512 Mon Sep 17 00:00:00 2001 From: jan madsen Date: Wed, 14 Aug 2019 19:07:30 +0000 Subject: [PATCH 02/43] Translated using Weblate (Danish) Currently translated at 96.6% (344 of 356 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/da/ --- app/src/main/res/values-da/strings.xml | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index e8e93bbad..0ec6751ba 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -125,7 +125,7 @@ Installer en web-browser til at åbne URL. Seneste databaser Gennemsøg ikke backup poster - Udelad \"Backup\" - gruppe fra søgeresultaterne (gælder kun for .kdb filer) + Udelader \"Backup\" og \"Papirkurv\" - grupper fra søgeresultater Opretter ny database… Arbejder… Beskyttelse @@ -188,7 +188,7 @@ Kunne ikke finde filen. Prøv at åbne den fra filhåndtering. Vis brugernavne Vis brugernavne i postlister - Kopieret %1$s + Kopi af %1$s Formularudfyldning Kopier Flyt @@ -216,7 +216,7 @@ Brug Android Storage Access (SAF) som filhåndtering (KitKat og senere) Storage Access Framework Advarsel - Undgå adgangskodetegn undenfor Latin-1 tegnsæt i .kdb filer, da de alle konverteres til det samme bogstav. + Undgå adgangskodetegn uden for tekstkodningsformatet i databasefilen (ukendte tegn konverteres til samme bogstav). Bekræft brug af ingen adgangskode til beskyttelse mod oplåsning\? Bekræft ingen brug af en krypteringsnøgle? Fingeraftryksscanning understøttes, men er ikke sat op. @@ -241,7 +241,7 @@ Angiv tilladte tegn for adgangskodegenerator Udklipsholder Udklipsholdermeddelelser - Aktivere meddelelser fra udklipsholder for at kopiere indtastningsfelter + Aktivér udklipsholder for at kopiere felter når en post vises Hvis automatisk sletning af udklipsholder mislykkes, slet historikken manuelt. Lås Skærmlås @@ -379,13 +379,13 @@ Magikeyboard indstillinger Post Timeout - Meddelsesinformation + Meddelelsesinfo Vis en meddelelse, når en post er til rådighed Post %1$s til rådighed på Magikeyboard %1$s Ryd ved lukning - Ryd tastatur post, når meddelse lukkes + Luk databasen, når meddelse lukkes Udseende Tastaturtema Taster @@ -393,4 +393,16 @@ Lyd ved tastetryk Build %1$s Timeout for at rydde indtastning + Noter + Valgstilstand + Luk ikke programmet… + Lås ved retur + Lås databasen, når brugeren klikker på tilbage-knappen fra startskærmen + Ryd ved lukning + Luk databasen ved lukning af underretning + Papirkurv + Valg af indtastning + Vis indtastningsfelter i Magikeyboard, når der vises en post + Slet adgangskode + Sletter adgangskoden som er indtastet efter et forbindelsesforsøg \ No newline at end of file From cde8950257fdb99e59850dc99f9b747fae02c178 Mon Sep 17 00:00:00 2001 From: Kunzisoft Date: Wed, 14 Aug 2019 18:29:01 +0000 Subject: [PATCH 03/43] Translated using Weblate (English) Currently translated at 99.7% (355 of 356 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/en/ --- app/src/main/res/values/strings.xml | 56 +++++++++-------------------- 1 file changed, 17 insertions(+), 39 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 09103a1c0..00b5ca3f8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,4 +1,4 @@ - + - +--> Feedback Homepage Android implementation of the KeePass password manager @@ -45,7 +44,7 @@ Clipboard timeout Duration of storage in the clipboard Swipe to clear clipboard now - + Select to copy %1$s to clipboard Retrieving database key… Database @@ -297,45 +296,32 @@ Fill in your fields using the entry elements. Lock the database. Use default keyboard again. - Magikeyboard Magikeyboard (KeePass DX) Magikeyboard settings - Entry - Entry selection Show input fields in Magikeyboard when viewing an entry - - Notification information + Notification info Show a notification when an entry is available - Clear at closing Close the database when closing the notification - Timeout Timeout to clear the keyboard entry - Entry %1$s available on Magikeyboard %1$s - Appearance - Keyboard theme - Keys Vibrate on keypress - Sound on keypress - Allow no password Enable the \"Open\" button if no password identification is selected Write-protected Open your database read-only by default Delete password Deletes the password entered after a connection attempt - Educational screens Highlight the elements to learn how the app works Reset educational screens @@ -372,12 +358,10 @@ Choose how entries and groups are sorted. Participate Help increase the stability, security and in adding more features. - Unlike many password management apps, this one is <strong>ad-free</strong>, <strong>copylefted libre software</strong> and does not collect personal data on its servers, no matter what version you use. By buying the pro version, you will have access to this <strong>visual feature</strong> and you will especially help <strong>the realization of community projects.</strong> This <strong>visual feature</strong> is available thanks to your generosity. In order to keep our freedom and to always be active, we count on your <strong>contribution.</strong> - This feature is <strong>under development</strong> and requires your <strong>contribution</strong> to be available soon. By buying the <strong>pro</strong> version, By <strong>contributing</strong>, @@ -385,36 +369,31 @@ Thanks a lot for your contribution. We are working hard to release this feature quickly. Do not forget to keep your app up to date by installing new versions. - Download Contribute - Rijndael (AES) Twofish ChaCha20 - AES KDF Argon2 - - 5 seconds - 10 seconds - 20 seconds - 30 seconds - 1 minute - 5 minutes - 15 minutes - 30 minutes - Never + 5 seconds + 10 seconds + 20 seconds + 30 seconds + 1 minute + 5 minutes + 15 minutes + 30 minutes + Never - Small - Medium - Large + Small + Medium + Large - App theme Theme used in the app @@ -427,5 +406,4 @@ Icon pack Icon pack used in the app - - + \ No newline at end of file From a83c60583fd8f1d0137dc046566551971e27a9fb Mon Sep 17 00:00:00 2001 From: Wilker Santana da Silva Date: Wed, 14 Aug 2019 22:53:44 +0000 Subject: [PATCH 04/43] Translated using Weblate (English) Currently translated at 99.7% (355 of 356 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/en/ --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 00b5ca3f8..f5af8ceca 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -231,7 +231,7 @@ Lock Screen lock Lock the database when the screen is off - Back lock + Press Back on root to lock Lock the database when the user clicks the back button on the root screen How to set up fingerprint scanning for quick unlocking? Save your scanned fingerprint for your device in From 6f513b4920ffa709ce98ae9bd9a59e39afe221ce Mon Sep 17 00:00:00 2001 From: Kunzisoft Date: Wed, 14 Aug 2019 18:23:02 +0000 Subject: [PATCH 05/43] Translated using Weblate (French) Currently translated at 99.4% (354 of 356 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/fr/ --- app/src/main/res/values-fr/strings.xml | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 40b7f9c7f..2813734b6 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -135,7 +135,7 @@ Ouvrir une base de données existante Bases de données récentes Ne pas rechercher dans les entrées de sauvegarde - Omet le groupe « Sauvegarde » des résultats de recherche (ne s’applique qu’aux fichiers .kdb) + Omet les groupes « Sauvegarde » et \"Corbeille\" des résultats de recherche Création d’une nouvelle base de données… Traitement en cours … Protection @@ -207,7 +207,7 @@ Définir les caractères autorisés du générateur de mot de passe Presse-papier Notifications du presse-papier - Activer les notifications du presse-papier pour copier les champs des entrées + Activer les notifications du presse-papier pour copier les champs lors de l\'affichage d\'une entrée Si la suppression automatique du presse-papier échoue, supprimer son historique manuellement. Verrouiller Verrouillage d’écran @@ -393,10 +393,21 @@ 1$s disponible sur Magikeyboard %1$s Effacer à la fermeture - Effacer l’entrée au clavier lors de la fermeture de la notification + Fermer la base de données lors de la fermeture de la notification Apparence Thème du clavier Touches Vibrer au toucher Son au toucher + Mode sélection + Ne pas tuer l\'application… + Déverouillage de retour + Verrouille la base de données lorsque l\'utilisateur clique sur le bouton Précédent de l\'écran racine + Suppression à la fermeture + Ferme la base de données lors de la fermeture de notification + Corbeille + Sélection d\'entrée + Afficher les champs de saisie dans Magikeyboard lors de l\'affichage d\'une entrée + Supprimer le mot de passe + Supprime le mot de passe entré après une tentative de connexion \ No newline at end of file From c303ffafb56ee02d006f90b3eac1c35bb71aa554 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Allan=20Nordh=C3=B8y?= Date: Wed, 14 Aug 2019 22:41:23 +0000 Subject: [PATCH 06/43] =?UTF-8?q?Translated=20using=20Weblate=20(Norwegian?= =?UTF-8?q?=20Bokm=C3=A5l)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 96.3% (343 of 356 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/nb_NO/ --- app/src/main/res/values-nb/strings.xml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index 64ef71f1f..5b2f9612e 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -356,4 +356,15 @@ Taster Vibrer ved tastetrykk Lyd ved tastetrykk + Valgmodus + Ikke drep programmet… + Tilbakelås + Lås databasen når brukeren klikker tilbakeknappen på root-skjermen + Tøm ved lukking + Lukk databasen ved lukking av merknaden + Papirkurv + Oppføringsvalg + Vis inndatafelter i Magikeyboard når en oppføring vises + Slett passord + Sletter passord innskrevet etter et tilkoblingsforsøk \ No newline at end of file From cbe7907b07807ca0c4fa078f4f90058d2ae18c98 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Fri, 16 Aug 2019 18:41:24 +0200 Subject: [PATCH 07/43] Upgrade version to 2.5.0.0beta20 --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index ad8fb759b..f1eed1aeb 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,8 +11,8 @@ android { applicationId "com.kunzisoft.keepass" minSdkVersion 14 targetSdkVersion 27 - versionCode = 19 - versionName = "2.5.0.0beta19" + versionCode = 20 + versionName = "2.5.0.0beta20" multiDexEnabled true testApplicationId = "com.kunzisoft.keepass.tests" From d9b2942f66f7d868ea96a4403b2247a0a6e5844d Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Fri, 16 Aug 2019 18:43:34 +0200 Subject: [PATCH 08/43] Fix Entry information is not correct #286 --- .../com/kunzisoft/keepass/database/element/PwDatabase.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.kt index e514e5f5b..e7ccf1f4a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.kt @@ -233,7 +233,9 @@ abstract class PwDatabase, Entry : PwEntry, Entry : PwEntry Date: Fri, 16 Aug 2019 19:17:59 +0200 Subject: [PATCH 09/43] Add changelog --- CHANGELOG | 3 +++ fastlane/metadata/android/en-US/changelogs/20.txt | 9 +++++++++ fastlane/metadata/android/fr-FR/changelogs/20.txt | 9 +++++++++ 3 files changed, 21 insertions(+) create mode 100644 fastlane/metadata/android/en-US/changelogs/20.txt create mode 100644 fastlane/metadata/android/fr-FR/changelogs/20.txt diff --git a/CHANGELOG b/CHANGELOG index e575c1ced..fbf1229e7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +KeepassDX (2.5.0.0beta20) + * Fix a major bug that displays an entry history + KeepassDX (2.5.0.0beta19) * Add lock button always visible * New connection workflow diff --git a/fastlane/metadata/android/en-US/changelogs/20.txt b/fastlane/metadata/android/en-US/changelogs/20.txt new file mode 100644 index 000000000..db1cd8608 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/20.txt @@ -0,0 +1,9 @@ + * Add lock button always visible + * New connection workflow + * Code refactored in Kotlin + * Better notification implementation + * Better views for large screen + * Magikeyboard enhancement + * Fix Recycle Bin + * Fix memory when load database + * Fix a bug that displays an entry history in 2.5.0.0beta19 \ No newline at end of file diff --git a/fastlane/metadata/android/fr-FR/changelogs/20.txt b/fastlane/metadata/android/fr-FR/changelogs/20.txt new file mode 100644 index 000000000..868a6f516 --- /dev/null +++ b/fastlane/metadata/android/fr-FR/changelogs/20.txt @@ -0,0 +1,9 @@ + * Ajout du bouton de lock toujours visible + * Nouveau workflow de connexion + * Code refactorisé en Kotlin + * Meilleure implementation des notifications + * Meilleures vues pour les écrans larges + * Amélioration du Magikeyboard + * Correction de la Corbeille + * Correction de la mémoire lors du chargement de base de données + * Correction d'un bug qui affiche un historique d'entrée (2.5.0.0beta19) \ No newline at end of file From 578da7bae584234b2b8df1bbfebcde46c7eb0629 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Fri, 16 Aug 2019 21:01:06 +0200 Subject: [PATCH 10/43] Fix copy_field error --- app/src/main/res/values-tr/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 9f3cd47b6..0239bf2ff 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -102,7 +102,7 @@ Parola maskesi. Varsayılan (***) Hakkında Ana anahtarı değitir - 1$s kopyalandı + %1$s kopyalandı Ayarlar Uygulama ayarları Form doldurma From 3ccfd7226b1f5129056e2705c987a9073e83682f Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sat, 17 Aug 2019 12:58:11 +0200 Subject: [PATCH 11/43] Upgrade to version 2.5.0.0beta21 and fix nested groups in v1 #292 --- CHANGELOG | 3 + app/build.gradle | 4 +- .../keepass/database/element/PwDatabaseV3.kt | 69 +++++++++---------- .../metadata/android/en-US/changelogs/21.txt | 9 +++ .../metadata/android/fr-FR/changelogs/21.txt | 9 +++ 5 files changed, 54 insertions(+), 40 deletions(-) create mode 100644 fastlane/metadata/android/en-US/changelogs/21.txt create mode 100644 fastlane/metadata/android/fr-FR/changelogs/21.txt diff --git a/CHANGELOG b/CHANGELOG index fbf1229e7..cb39b64e0 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +KeepassDX (2.5.0.0beta20) + * Fix nested groups no longer visible in V1 databases + KeepassDX (2.5.0.0beta20) * Fix a major bug that displays an entry history diff --git a/app/build.gradle b/app/build.gradle index f1eed1aeb..b0b10d4f7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,8 +11,8 @@ android { applicationId "com.kunzisoft.keepass" minSdkVersion 14 targetSdkVersion 27 - versionCode = 20 - versionName = "2.5.0.0beta20" + versionCode = 21 + versionName = "2.5.0.0beta21" multiDexEnabled true testApplicationId = "com.kunzisoft.keepass.tests" diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.kt index cd56208c7..c1e68a3b4 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.kt @@ -27,7 +27,6 @@ import java.io.InputStream import java.security.DigestOutputStream import java.security.MessageDigest import java.security.NoSuchAlgorithmException -import java.util.* /** * @author Naomaru Itoi @phoneid.org> @@ -76,50 +75,44 @@ class PwDatabaseV3 : PwDatabase() { numKeyEncRounds = DEFAULT_ENCRYPTION_ROUNDS } - private fun assignGroupsChildren(parent: PwGroupV3) { - val levelToCheck = parent.level + 1 - var startFromParentPosition = false - for (groupToCheck in getGroupIndexes()) { - rootGroup?.let { root -> - if (root.nodeId == parent.nodeId || groupToCheck.nodeId == parent.nodeId) { - startFromParentPosition = true - } - } - if (startFromParentPosition) { - if (groupToCheck.level < levelToCheck) - break - else if (groupToCheck.level == levelToCheck) - parent.addChildGroup(groupToCheck) - } + private fun buildTreeGroups(previousGroup: PwGroupV3, currentGroup: PwGroupV3, groupIterator: Iterator) { + + if (currentGroup.parent == null && (previousGroup.level + 1) == currentGroup.level) { + // 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) { + // 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) } - } - private fun assignEntriesChildren(parent: PwGroupV3) { - for (entry in getEntryIndexes()) { - if (entry.parent!!.nodeId == parent.nodeId) - parent.addChildEntry(entry) - } - } - - private fun constructTreeFromIndex(currentGroup: PwGroupV3) { - - assignGroupsChildren(currentGroup) - assignEntriesChildren(currentGroup) - - // set parent in child entries (normally useless but to be sure or to update parent metadata) - for (childEntry in currentGroup.getChildEntries()) { - childEntry.parent = currentGroup - } - // recursively construct child groups - for (childGroup in currentGroup.getChildGroups()) { - childGroup.parent = currentGroup - constructTreeFromIndex(childGroup) + // Next current group + if (groupIterator.hasNext()){ + buildTreeGroups(currentGroup, groupIterator.next(), groupIterator) } } fun constructTreeFromIndex() { rootGroup?.let { - constructTreeFromIndex(it) + + // add each group + val groupIterator = getGroupIndexes().iterator() + if (groupIterator.hasNext()) + buildTreeGroups(it, groupIterator.next(), groupIterator) + + // add each child + for (currentEntry in getEntryIndexes()) { + if (currentEntry.parent != null) { + // Only the parent id is known so complete the info + val parentGroupRetrieve = getGroupById(currentEntry.parent!!.nodeId) + parentGroupRetrieve?.addChildEntry(currentEntry) + currentEntry.parent = parentGroupRetrieve + } + } } } diff --git a/fastlane/metadata/android/en-US/changelogs/21.txt b/fastlane/metadata/android/en-US/changelogs/21.txt new file mode 100644 index 000000000..6cf34ea25 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/21.txt @@ -0,0 +1,9 @@ + * Add lock button always visible + * New connection workflow + * Code refactored in Kotlin + * Better notification implementation + * Better views for large screen + * Magikeyboard enhancement + * Fix Recycle Bin + * Fix memory when load database + * Fix bugs that displays bad entries and groups (2.5.0.0beta19-20) \ No newline at end of file diff --git a/fastlane/metadata/android/fr-FR/changelogs/21.txt b/fastlane/metadata/android/fr-FR/changelogs/21.txt new file mode 100644 index 000000000..f703e3393 --- /dev/null +++ b/fastlane/metadata/android/fr-FR/changelogs/21.txt @@ -0,0 +1,9 @@ + * Ajout du bouton de lock toujours visible + * Nouveau workflow de connexion + * Code refactorisé en Kotlin + * Meilleure implementation des notifications + * Meilleures vues pour les écrans larges + * Amélioration du Magikeyboard + * Correction de la Corbeille + * Correction de la mémoire lors du chargement de base de données + * Correction de bugs d'affichage des entrée et groupes (2.5.0.0beta19-20) \ No newline at end of file From 722ba5c34d196584f64645c2de085d80a55bc1d7 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sat, 17 Aug 2019 13:20:53 +0200 Subject: [PATCH 12/43] Move construct tree methods in Importer --- .../keepass/database/element/PwDatabaseV3.kt | 41 ------------------ .../keepass/database/file/load/ImporterV3.kt | 43 ++++++++++++++++++- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.kt index c1e68a3b4..7a042a13d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.kt @@ -75,47 +75,6 @@ class PwDatabaseV3 : PwDatabase() { numKeyEncRounds = DEFAULT_ENCRYPTION_ROUNDS } - private fun buildTreeGroups(previousGroup: PwGroupV3, currentGroup: PwGroupV3, groupIterator: Iterator) { - - if (currentGroup.parent == null && (previousGroup.level + 1) == currentGroup.level) { - // 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) { - // 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) - } - - // Next current group - if (groupIterator.hasNext()){ - buildTreeGroups(currentGroup, groupIterator.next(), groupIterator) - } - } - - fun constructTreeFromIndex() { - rootGroup?.let { - - // add each group - val groupIterator = getGroupIndexes().iterator() - if (groupIterator.hasNext()) - buildTreeGroups(it, groupIterator.next(), groupIterator) - - // add each child - for (currentEntry in getEntryIndexes()) { - if (currentEntry.parent != null) { - // Only the parent id is known so complete the info - val parentGroupRetrieve = getGroupById(currentEntry.parent!!.nodeId) - parentGroupRetrieve?.addChildEntry(currentEntry) - currentEntry.parent = parentGroupRetrieve - } - } - } - } - /** * Generates an unused random tree id * diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/load/ImporterV3.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/load/ImporterV3.kt index 2c72ce9dc..ed6fb928c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/load/ImporterV3.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/load/ImporterV3.kt @@ -223,11 +223,52 @@ class ImporterV3 : Importer() { pos += 2 + 4 + fieldSize } - mDatabaseToOpen.constructTreeFromIndex() + constructTreeFromIndex() return mDatabaseToOpen } + private fun buildTreeGroups(previousGroup: PwGroupV3, currentGroup: PwGroupV3, groupIterator: Iterator) { + + if (currentGroup.parent == null && (previousGroup.level + 1) == currentGroup.level) { + // 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) { + // 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) + } + + // Next current group + if (groupIterator.hasNext()){ + buildTreeGroups(currentGroup, groupIterator.next(), groupIterator) + } + } + + private fun constructTreeFromIndex() { + mDatabaseToOpen.rootGroup?.let { + + // add each group + val groupIterator = mDatabaseToOpen.getGroupIndexes().iterator() + if (groupIterator.hasNext()) + buildTreeGroups(it, groupIterator.next(), groupIterator) + + // add each child + for (currentEntry in mDatabaseToOpen.getEntryIndexes()) { + if (currentEntry.parent != null) { + // Only the parent id is known so complete the info + val parentGroupRetrieve = mDatabaseToOpen.getGroupById(currentEntry.parent!!.nodeId) + parentGroupRetrieve?.addChildEntry(currentEntry) + currentEntry.parent = parentGroupRetrieve + } + } + } + } + /** * Parse and save one record from binary file. * @param buf From 8536de35556067270db521216999a43f1aade6eb Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sat, 17 Aug 2019 13:21:48 +0200 Subject: [PATCH 13/43] Remove email (no more code) --- .../com/kunzisoft/keepass/database/file/load/ImporterV3.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/load/ImporterV3.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/load/ImporterV3.kt index ed6fb928c..3771f2d09 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/load/ImporterV3.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/load/ImporterV3.kt @@ -68,9 +68,6 @@ import java.util.Arrays /** * Load a v3 database file. - * - * @author Naomaru Itoi @phoneid.org> - * @author Bill Zwicky @pobox.com> */ class ImporterV3 : Importer() { From 173dd5b59b7d693fca35d9fdd55f3464bcbb2139 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sat, 17 Aug 2019 14:29:18 +0200 Subject: [PATCH 14/43] Add natural database sort --- CHANGELOG | 1 + .../kunzisoft/keepass/adapters/NodeAdapter.kt | 3 +- .../keepass/database/SortNodeEnum.kt | 178 ++++++++---------- .../database/element/GroupVersioned.kt | 7 +- .../keepass/database/element/NodeVersioned.kt | 14 +- .../res/layout/fragment_sort_selection.xml | 3 +- .../metadata/android/en-US/changelogs/21.txt | 11 +- .../metadata/android/fr-FR/changelogs/21.txt | 11 +- 8 files changed, 101 insertions(+), 127 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index cb39b64e0..6f468d440 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,6 @@ KeepassDX (2.5.0.0beta20) * Fix nested groups no longer visible in V1 databases + * Add natural database sort KeepassDX (2.5.0.0beta20) * Fix a major bug that displays an entry history diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.kt b/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.kt index 3053fe500..496d52b2c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.kt +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.kt @@ -30,7 +30,6 @@ import android.widget.ImageView import android.widget.TextView import android.widget.Toast import com.kunzisoft.keepass.R -import com.kunzisoft.keepass.app.App import com.kunzisoft.keepass.database.SortNodeEnum import com.kunzisoft.keepass.database.element.* import com.kunzisoft.keepass.icons.assignDatabaseIcon @@ -138,7 +137,7 @@ class NodeAdapter assignPreferences() // TODO verify sort try { - this.nodeSortedList.addAll(group.getChildrenWithoutMetaStream()) + this.nodeSortedList.addAll(group.getChildren()) } catch (e: Exception) { Log.e(TAG, "Can't add node elements to the list", e) Toast.makeText(context, "Can't add node elements to the list : " + e.message, Toast.LENGTH_LONG).show() diff --git a/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.kt b/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.kt index 22a6b2415..e579df22e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.kt @@ -31,7 +31,7 @@ enum class SortNodeEnum { fun getNodeComparator(ascending: Boolean, groupsBefore: Boolean): Comparator { return when (this) { - DB -> NodeCreationComparator(ascending, groupsBefore) // TODO Sort + DB -> NodeNaturalComparator(ascending, groupsBefore) TITLE -> NodeTitleComparator(ascending, groupsBefore) USERNAME -> NodeCreationComparator(ascending, groupsBefore) // TODO Sort CREATION_TIME -> NodeCreationComparator(ascending, groupsBefore) @@ -40,7 +40,7 @@ enum class SortNodeEnum { } } - abstract class NodeComparator internal constructor(internal var ascending: Boolean, private var groupsBefore: Boolean) : Comparator { + abstract class NodeComparator(internal var ascending: Boolean, private var groupsBefore: Boolean) : Comparator { internal fun compareWith(comparatorGroup: Comparator, comparatorEntry: Comparator, @@ -81,10 +81,27 @@ enum class SortNodeEnum { } } + /** + * Comparator of node by natural database placement + */ + class NodeNaturalComparator(ascending: Boolean, groupsBefore: Boolean) : NodeComparator(ascending, groupsBefore) { + + override fun compare(object1: NodeVersioned, object2: NodeVersioned): Int { + + return compareWith( + GroupNaturalOrderComparator(ascending), + EntryNaturalOrderComparator(ascending), + object1, + object2, + object1.creationTime.date + ?.compareTo(object2.creationTime.date) ?: 0) + } + } + /** * Comparator of Node by Title, Groups first, Entries second */ - class NodeTitleComparator internal constructor(ascending: Boolean, groupsBefore: Boolean) : NodeComparator(ascending, groupsBefore) { + class NodeTitleComparator(ascending: Boolean, groupsBefore: Boolean) : NodeComparator(ascending, groupsBefore) { override fun compare(object1: NodeVersioned, object2: NodeVersioned): Int { @@ -101,7 +118,7 @@ enum class SortNodeEnum { /** * Comparator of node by creation, Groups first, Entries second */ - class NodeCreationComparator internal constructor(ascending: Boolean, groupsBefore: Boolean) : NodeComparator(ascending, groupsBefore) { + class NodeCreationComparator(ascending: Boolean, groupsBefore: Boolean) : NodeComparator(ascending, groupsBefore) { override fun compare(object1: NodeVersioned, object2: NodeVersioned): Int { @@ -118,7 +135,7 @@ enum class SortNodeEnum { /** * Comparator of node by last modification, Groups first, Entries second */ - class NodeLastModificationComparator internal constructor(ascending: Boolean, groupsBefore: Boolean) : NodeComparator(ascending, groupsBefore) { + class NodeLastModificationComparator(ascending: Boolean, groupsBefore: Boolean) : NodeComparator(ascending, groupsBefore) { override fun compare(object1: NodeVersioned, object2: NodeVersioned): Int { @@ -135,7 +152,7 @@ enum class SortNodeEnum { /** * Comparator of node by last access, Groups first, Entries second */ - class NodeLastAccessComparator internal constructor(ascending: Boolean, groupsBefore: Boolean) : NodeComparator(ascending, groupsBefore) { + class NodeLastAccessComparator(ascending: Boolean, groupsBefore: Boolean) : NodeComparator(ascending, groupsBefore) { override fun compare(object1: NodeVersioned, object2: NodeVersioned): Int { @@ -149,162 +166,119 @@ enum class SortNodeEnum { } } - abstract class AscendingComparator internal constructor(private val ascending: Boolean) : Comparator { + abstract class AscendingComparator(private val ascending: Boolean) : Comparator { - internal fun compareWithAscending(basicCompareResult: Int): Int { + private fun compareWithAscending(specificOrderComp: Int): Int { // If descending, revert - return if (!ascending) -basicCompareResult else basicCompareResult - + return if (!ascending) -specificOrderComp else specificOrderComp } - } - /** - * Group comparator by name - */ - class GroupNameComparator internal constructor(ascending: Boolean) : AscendingComparator(ascending) { + abstract fun compareBySecificOrder(object1: Node, object2: Node): Int - override fun compare(object1: GroupVersioned, object2: GroupVersioned): Int { + override fun compare(object1: Node, object2: Node): Int { if (object1 == object2) return 0 - val groupNameComp = object1.title.compareTo(object2.title, ignoreCase = true) - // If same name, can be different - return if (groupNameComp == 0) { + val specificOrderComp = compareBySecificOrder(object1, object2) + return if (specificOrderComp == 0) { object1.hashCode() - object2.hashCode() - } else compareWithAscending(groupNameComp) + } else compareWithAscending(specificOrderComp) + } + } + /** + * Group comparator by natural order + */ + class GroupNaturalOrderComparator(ascending: Boolean) : AscendingComparator(ascending) { + override fun compareBySecificOrder(object1: GroupVersioned, object2: GroupVersioned): Int { + return object1.nodePositionInParent.compareTo(object2.nodePositionInParent) } } /** * Group comparator by name */ - class GroupCreationComparator internal constructor(ascending: Boolean) : AscendingComparator(ascending) { + class GroupNameComparator(ascending: Boolean) : AscendingComparator(ascending) { + override fun compareBySecificOrder(object1: GroupVersioned, object2: GroupVersioned): Int { + return object1.title.compareTo(object2.title, ignoreCase = true) + } + } - override fun compare(object1: GroupVersioned, object2: GroupVersioned): Int { - if (object1 == object2) - return 0 - - val groupCreationComp = object1.creationTime.date + /** + * Group comparator by name + */ + class GroupCreationComparator(ascending: Boolean) : AscendingComparator(ascending) { + override fun compareBySecificOrder(object1: GroupVersioned, object2: GroupVersioned): Int { + return object1.creationTime.date ?.compareTo(object2.creationTime.date) ?: 0 - // If same creation, can be different - return if (groupCreationComp == 0) { - object1.hashCode() - object2.hashCode() - } else compareWithAscending(groupCreationComp) - } } /** * Group comparator by last modification */ - class GroupLastModificationComparator internal constructor(ascending: Boolean) : AscendingComparator(ascending) { - - override fun compare(object1: GroupVersioned, object2: GroupVersioned): Int { - if (object1 == object2) - return 0 - - val groupLastModificationComp = object1.lastModificationTime.date + class GroupLastModificationComparator(ascending: Boolean) : AscendingComparator(ascending) { + override fun compareBySecificOrder(object1: GroupVersioned, object2: GroupVersioned): Int { + return object1.lastModificationTime.date ?.compareTo(object2.lastModificationTime.date) ?: 0 - // If same creation, can be different - return if (groupLastModificationComp == 0) { - object1.hashCode() - object2.hashCode() - } else compareWithAscending(groupLastModificationComp) - } } /** * Group comparator by last access */ - class GroupLastAccessComparator internal constructor(ascending: Boolean) : AscendingComparator(ascending) { - - override fun compare(object1: GroupVersioned, object2: GroupVersioned): Int { - if (object1 == object2) - return 0 - - val groupLastAccessComp = object1.lastAccessTime.date + class GroupLastAccessComparator(ascending: Boolean) : AscendingComparator(ascending) { + override fun compareBySecificOrder(object1: GroupVersioned, object2: GroupVersioned): Int { + return object1.lastAccessTime.date ?.compareTo(object2.lastAccessTime.date) ?: 0 - // If same creation, can be different - return if (groupLastAccessComp == 0) { - object1.hashCode() - object2.hashCode() - } else compareWithAscending(groupLastAccessComp) - } } /** * Comparator of Entry by Name */ - class EntryNameComparator internal constructor(ascending: Boolean) : AscendingComparator(ascending) { - - override fun compare(object1: EntryVersioned, object2: EntryVersioned): Int { - if (object1 == object2) - return 0 - - val entryTitleComp = object1.title.compareTo(object2.title, ignoreCase = true) - // If same title, can be different - return if (entryTitleComp == 0) { - object1.hashCode() - object2.hashCode() - } else compareWithAscending(entryTitleComp) + class EntryNaturalOrderComparator(ascending: Boolean) : AscendingComparator(ascending) { + override fun compareBySecificOrder(object1: EntryVersioned, object2: EntryVersioned): Int { + return object1.nodePositionInParent.compareTo(object2.nodePositionInParent) + } + } + /** + * Comparator of Entry by Name + */ + class EntryNameComparator(ascending: Boolean) : AscendingComparator(ascending) { + override fun compareBySecificOrder(object1: EntryVersioned, object2: EntryVersioned): Int { + return object1.title.compareTo(object2.title, ignoreCase = true) } } /** * Comparator of Entry by Creation */ - class EntryCreationComparator internal constructor(ascending: Boolean) : AscendingComparator(ascending) { - - override fun compare(object1: EntryVersioned, object2: EntryVersioned): Int { - if (object1 == object2) - return 0 - - val entryCreationComp = object1.creationTime.date + class EntryCreationComparator(ascending: Boolean) : AscendingComparator(ascending) { + override fun compareBySecificOrder(object1: EntryVersioned, object2: EntryVersioned): Int { + return object1.creationTime.date ?.compareTo(object2.creationTime.date) ?: 0 - // If same creation, can be different - return if (entryCreationComp == 0) { - object1.hashCode() - object2.hashCode() - } else compareWithAscending(entryCreationComp) - } } /** * Comparator of Entry by Last Modification */ - class EntryLastModificationComparator internal constructor(ascending: Boolean) : AscendingComparator(ascending) { - - override fun compare(object1: EntryVersioned, object2: EntryVersioned): Int { - if (object1 == object2) - return 0 - - val entryLastModificationComp = object1.lastModificationTime.date + class EntryLastModificationComparator(ascending: Boolean) : AscendingComparator(ascending) { + override fun compareBySecificOrder(object1: EntryVersioned, object2: EntryVersioned): Int { + return object1.lastModificationTime.date ?.compareTo(object2.lastModificationTime.date) ?: 0 - // If same creation, can be different - return if (entryLastModificationComp == 0) { - object1.hashCode() - object2.hashCode() - } else compareWithAscending(entryLastModificationComp) - } } /** * Comparator of Entry by Last Access */ - class EntryLastAccessComparator internal constructor(ascending: Boolean) : AscendingComparator(ascending) { - - override fun compare(object1: EntryVersioned, object2: EntryVersioned): Int { - if (object1 == object2) - return 0 - - val entryLastAccessComp = object1.lastAccessTime.date + class EntryLastAccessComparator(ascending: Boolean) : AscendingComparator(ascending) { + override fun compareBySecificOrder(object1: EntryVersioned, object2: EntryVersioned): Int { + return object1.lastAccessTime.date ?.compareTo(object2.lastAccessTime.date) ?: 0 - // If same creation, can be different - return if (entryLastAccessComp == 0) { - object1.hashCode() - object2.hashCode() - } else compareWithAscending(entryLastAccessComp) - } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/GroupVersioned.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/GroupVersioned.kt index 8ee77d8f5..22c179e43 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/GroupVersioned.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/GroupVersioned.kt @@ -195,12 +195,15 @@ class GroupVersioned : NodeVersioned, PwGroupInterface { + fun getChildren(withoutMetaStream: Boolean = true): List { val children = ArrayList() children.addAll(getChildGroups()) pwGroupV3?.let { - children.addAll(getChildEntries().filter { !it.isMetaStream }) + if (withoutMetaStream) + children.addAll(getChildEntries().filter { !it.isMetaStream }) + else + children.addAll(getChildEntries()) } pwGroupV4?.let { // No MetasStream in V4 diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/NodeVersioned.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/NodeVersioned.kt index c0de233e9..44a3746dc 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/NodeVersioned.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/NodeVersioned.kt @@ -1,6 +1,18 @@ package com.kunzisoft.keepass.database.element -interface NodeVersioned: PwNodeInterface +interface NodeVersioned: PwNodeInterface { + + val nodePositionInParent: Int + get() { + parent?.getChildren(false)?.let { children -> + for ((i, child) in children.withIndex()) { + if (child == this) + return i + } + } + return -1 + } +} /** * Type of available Nodes diff --git a/app/src/main/res/layout/fragment_sort_selection.xml b/app/src/main/res/layout/fragment_sort_selection.xml index 0c4326f0d..6dd0fe837 100644 --- a/app/src/main/res/layout/fragment_sort_selection.xml +++ b/app/src/main/res/layout/fragment_sort_selection.xml @@ -15,8 +15,7 @@ + android:text="@string/sort_db"/> Date: Sat, 17 Aug 2019 15:15:21 +0200 Subject: [PATCH 15/43] Better sort implementation --- .../keepass/database/SortNodeEnum.kt | 216 +++--------------- 1 file changed, 35 insertions(+), 181 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.kt b/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.kt index e579df22e..75957a73a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.kt @@ -20,11 +20,9 @@ package com.kunzisoft.keepass.database -import com.kunzisoft.keepass.database.element.EntryVersioned -import com.kunzisoft.keepass.database.element.GroupVersioned import com.kunzisoft.keepass.database.element.NodeVersioned - -import java.util.Comparator +import com.kunzisoft.keepass.database.element.Type +import java.util.* enum class SortNodeEnum { DB, TITLE, USERNAME, CREATION_TIME, LAST_MODIFY_TIME, LAST_ACCESS_TIME; @@ -40,21 +38,26 @@ enum class SortNodeEnum { } } - abstract class NodeComparator(internal var ascending: Boolean, private var groupsBefore: Boolean) : Comparator { + abstract class NodeComparator(var ascending: Boolean, var groupsBefore: Boolean) : Comparator { - internal fun compareWith(comparatorGroup: Comparator, - comparatorEntry: Comparator, - object1: NodeVersioned, - object2: NodeVersioned, - resultOfNodeMethodCompare: Int): Int { + abstract fun compareBySpecificOrder(object1: NodeVersioned, object2: NodeVersioned): Int + + private fun specificOrderOrHashIfEquals(object1: NodeVersioned, object2: NodeVersioned): Int { + val specificOrderComp = compareBySpecificOrder(object1, object2) + + return if (specificOrderComp == 0) { + object1.hashCode() - object2.hashCode() + } else if (!ascending) -specificOrderComp else specificOrderComp // If descending, revert + } + + override fun compare(object1: NodeVersioned,object2: NodeVersioned): Int { if (object1 == object2) return 0 - if (object1 is GroupVersioned) { - return if (object2 is GroupVersioned) { - comparatorGroup - .compare(object1, object2) - } else if (object2 is EntryVersioned) { + if (object1.type == Type.GROUP) { + return if (object2.type == Type.GROUP) { + specificOrderOrHashIfEquals(object1, object2) + } else if (object2.type == Type.ENTRY) { if (groupsBefore) -1 else @@ -62,11 +65,10 @@ enum class SortNodeEnum { } else { -1 } - } else if (object1 is EntryVersioned) { - return if (object2 is EntryVersioned) { - comparatorEntry - .compare(object1, object2) - } else if (object2 is GroupVersioned) { + } else if (object1.type == Type.ENTRY) { + return if (object2.type == Type.ENTRY) { + specificOrderOrHashIfEquals(object1, object2) + } else if (object2.type == Type.GROUP) { if (groupsBefore) 1 else @@ -76,8 +78,8 @@ enum class SortNodeEnum { } } - // If same name, can be different - return if (resultOfNodeMethodCompare == 0) object1.hashCode() - object2.hashCode() else resultOfNodeMethodCompare + // Type not known + return -1 } } @@ -86,15 +88,8 @@ enum class SortNodeEnum { */ class NodeNaturalComparator(ascending: Boolean, groupsBefore: Boolean) : NodeComparator(ascending, groupsBefore) { - override fun compare(object1: NodeVersioned, object2: NodeVersioned): Int { - - return compareWith( - GroupNaturalOrderComparator(ascending), - EntryNaturalOrderComparator(ascending), - object1, - object2, - object1.creationTime.date - ?.compareTo(object2.creationTime.date) ?: 0) + override fun compareBySpecificOrder(object1: NodeVersioned, object2: NodeVersioned): Int { + return object1.nodePositionInParent.compareTo(object2.nodePositionInParent) } } @@ -103,15 +98,8 @@ enum class SortNodeEnum { */ class NodeTitleComparator(ascending: Boolean, groupsBefore: Boolean) : NodeComparator(ascending, groupsBefore) { - override fun compare(object1: NodeVersioned, object2: NodeVersioned): Int { - - return compareWith( - GroupNameComparator(ascending), - EntryNameComparator(ascending), - object1, - object2, - object1.title - .compareTo(object2.title, ignoreCase = true)) + override fun compareBySpecificOrder(object1: NodeVersioned, object2: NodeVersioned): Int { + return object1.title.compareTo(object2.title, ignoreCase = true) } } @@ -120,15 +108,9 @@ enum class SortNodeEnum { */ class NodeCreationComparator(ascending: Boolean, groupsBefore: Boolean) : NodeComparator(ascending, groupsBefore) { - override fun compare(object1: NodeVersioned, object2: NodeVersioned): Int { - - return compareWith( - GroupCreationComparator(ascending), - EntryCreationComparator(ascending), - object1, - object2, - object1.creationTime.date - ?.compareTo(object2.creationTime.date) ?: 0) + override fun compareBySpecificOrder(object1: NodeVersioned, object2: NodeVersioned): Int { + return object1.creationTime.date + ?.compareTo(object2.creationTime.date) ?: 0 } } @@ -137,15 +119,9 @@ enum class SortNodeEnum { */ class NodeLastModificationComparator(ascending: Boolean, groupsBefore: Boolean) : NodeComparator(ascending, groupsBefore) { - override fun compare(object1: NodeVersioned, object2: NodeVersioned): Int { - - return compareWith( - GroupLastModificationComparator(ascending), - EntryLastModificationComparator(ascending), - object1, - object2, - object1.lastModificationTime.date - ?.compareTo(object2.lastModificationTime.date) ?: 0) + override fun compareBySpecificOrder(object1: NodeVersioned, object2: NodeVersioned): Int { + return object1.lastModificationTime.date + ?.compareTo(object2.lastModificationTime.date) ?: 0 } } @@ -154,129 +130,7 @@ enum class SortNodeEnum { */ class NodeLastAccessComparator(ascending: Boolean, groupsBefore: Boolean) : NodeComparator(ascending, groupsBefore) { - override fun compare(object1: NodeVersioned, object2: NodeVersioned): Int { - - return compareWith( - GroupLastAccessComparator(ascending), - EntryLastAccessComparator(ascending), - object1, - object2, - object1.lastAccessTime.date - ?.compareTo(object2.lastAccessTime.date) ?: 0) - } - } - - abstract class AscendingComparator(private val ascending: Boolean) : Comparator { - - private fun compareWithAscending(specificOrderComp: Int): Int { - // If descending, revert - return if (!ascending) -specificOrderComp else specificOrderComp - } - - abstract fun compareBySecificOrder(object1: Node, object2: Node): Int - - override fun compare(object1: Node, object2: Node): Int { - if (object1 == object2) - return 0 - - val specificOrderComp = compareBySecificOrder(object1, object2) - return if (specificOrderComp == 0) { - object1.hashCode() - object2.hashCode() - } else compareWithAscending(specificOrderComp) - } - } - - /** - * Group comparator by natural order - */ - class GroupNaturalOrderComparator(ascending: Boolean) : AscendingComparator(ascending) { - override fun compareBySecificOrder(object1: GroupVersioned, object2: GroupVersioned): Int { - return object1.nodePositionInParent.compareTo(object2.nodePositionInParent) - } - } - - /** - * Group comparator by name - */ - class GroupNameComparator(ascending: Boolean) : AscendingComparator(ascending) { - override fun compareBySecificOrder(object1: GroupVersioned, object2: GroupVersioned): Int { - return object1.title.compareTo(object2.title, ignoreCase = true) - } - } - - /** - * Group comparator by name - */ - class GroupCreationComparator(ascending: Boolean) : AscendingComparator(ascending) { - override fun compareBySecificOrder(object1: GroupVersioned, object2: GroupVersioned): Int { - return object1.creationTime.date - ?.compareTo(object2.creationTime.date) ?: 0 - } - } - - /** - * Group comparator by last modification - */ - class GroupLastModificationComparator(ascending: Boolean) : AscendingComparator(ascending) { - override fun compareBySecificOrder(object1: GroupVersioned, object2: GroupVersioned): Int { - return object1.lastModificationTime.date - ?.compareTo(object2.lastModificationTime.date) ?: 0 - } - } - - /** - * Group comparator by last access - */ - class GroupLastAccessComparator(ascending: Boolean) : AscendingComparator(ascending) { - override fun compareBySecificOrder(object1: GroupVersioned, object2: GroupVersioned): Int { - return object1.lastAccessTime.date - ?.compareTo(object2.lastAccessTime.date) ?: 0 - } - } - - /** - * Comparator of Entry by Name - */ - class EntryNaturalOrderComparator(ascending: Boolean) : AscendingComparator(ascending) { - override fun compareBySecificOrder(object1: EntryVersioned, object2: EntryVersioned): Int { - return object1.nodePositionInParent.compareTo(object2.nodePositionInParent) - } - } - - /** - * Comparator of Entry by Name - */ - class EntryNameComparator(ascending: Boolean) : AscendingComparator(ascending) { - override fun compareBySecificOrder(object1: EntryVersioned, object2: EntryVersioned): Int { - return object1.title.compareTo(object2.title, ignoreCase = true) - } - } - - /** - * Comparator of Entry by Creation - */ - class EntryCreationComparator(ascending: Boolean) : AscendingComparator(ascending) { - override fun compareBySecificOrder(object1: EntryVersioned, object2: EntryVersioned): Int { - return object1.creationTime.date - ?.compareTo(object2.creationTime.date) ?: 0 - } - } - - /** - * Comparator of Entry by Last Modification - */ - class EntryLastModificationComparator(ascending: Boolean) : AscendingComparator(ascending) { - override fun compareBySecificOrder(object1: EntryVersioned, object2: EntryVersioned): Int { - return object1.lastModificationTime.date - ?.compareTo(object2.lastModificationTime.date) ?: 0 - } - } - - /** - * Comparator of Entry by Last Access - */ - class EntryLastAccessComparator(ascending: Boolean) : AscendingComparator(ascending) { - override fun compareBySecificOrder(object1: EntryVersioned, object2: EntryVersioned): Int { + override fun compareBySpecificOrder(object1: NodeVersioned, object2: NodeVersioned): Int { return object1.lastAccessTime.date ?.compareTo(object2.lastAccessTime.date) ?: 0 } From 459606f5d5ac6a29c47934c147a70a6abb270316 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sat, 17 Aug 2019 16:34:56 +0200 Subject: [PATCH 16/43] Fix sort for natural database --- .../activities/dialogs/SortDialogFragment.kt | 13 +++------ .../keepass/database/SortNodeEnum.kt | 28 +++++++++++++++---- .../database/element/EntryVersioned.kt | 23 +++++++++++---- .../keepass/settings/PreferencesUtil.kt | 2 +- .../res/layout/fragment_sort_selection.xml | 3 +- 5 files changed, 47 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/SortDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/SortDialogFragment.kt index b7d0859db..9f6c58cdc 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/SortDialogFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/SortDialogFragment.kt @@ -35,12 +35,12 @@ class SortDialogFragment : DialogFragment() { private var mListener: SortSelectionListener? = null - private var mSortNodeEnum: SortNodeEnum? = null + private var mSortNodeEnum: SortNodeEnum = SortNodeEnum.DB @IdRes private var mCheckedId: Int = 0 - private var mGroupsBefore: Boolean = false - private var mAscending: Boolean = false - private var mRecycleBinBottom: Boolean = false + private var mGroupsBefore: Boolean = true + private var mAscending: Boolean = true + private var mRecycleBinBottom: Boolean = true override fun onAttach(context: Context?) { super.onAttach(context) @@ -50,18 +50,13 @@ class SortDialogFragment : DialogFragment() { throw ClassCastException(context!!.toString() + " must implement " + SortSelectionListener::class.java.name) } - } override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { activity?.let { activity -> val builder = AlertDialog.Builder(activity) - mSortNodeEnum = SortNodeEnum.TITLE - mAscending = true - mGroupsBefore = true var recycleBinAllowed = false - mRecycleBinBottom = true arguments?.apply { if (containsKey(SORT_NODE_ENUM_BUNDLE_KEY)) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.kt b/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.kt index 75957a73a..c15c20614 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.kt @@ -20,6 +20,8 @@ package com.kunzisoft.keepass.database +import com.kunzisoft.keepass.database.element.Database +import com.kunzisoft.keepass.database.element.EntryVersioned import com.kunzisoft.keepass.database.element.NodeVersioned import com.kunzisoft.keepass.database.element.Type import java.util.* @@ -31,7 +33,7 @@ enum class SortNodeEnum { return when (this) { DB -> NodeNaturalComparator(ascending, groupsBefore) TITLE -> NodeTitleComparator(ascending, groupsBefore) - USERNAME -> NodeCreationComparator(ascending, groupsBefore) // TODO Sort + USERNAME -> NodeUsernameComparator(ascending, groupsBefore) CREATION_TIME -> NodeCreationComparator(ascending, groupsBefore) LAST_MODIFY_TIME -> NodeLastModificationComparator(ascending, groupsBefore) LAST_ACCESS_TIME -> NodeLastAccessComparator(ascending, groupsBefore) @@ -94,7 +96,7 @@ enum class SortNodeEnum { } /** - * Comparator of Node by Title, Groups first, Entries second + * Comparator of Node by Title */ class NodeTitleComparator(ascending: Boolean, groupsBefore: Boolean) : NodeComparator(ascending, groupsBefore) { @@ -104,7 +106,23 @@ enum class SortNodeEnum { } /** - * Comparator of node by creation, Groups first, Entries second + * Comparator of Node by Username, Groups by title + */ + class NodeUsernameComparator(ascending: Boolean, groupsBefore: Boolean) : NodeComparator(ascending, groupsBefore) { + + override fun compareBySpecificOrder(object1: NodeVersioned, object2: NodeVersioned): Int { + if (object1.type == Type.ENTRY && object2.type == Type.ENTRY) { + // To get username if it's a ref + return (object1 as EntryVersioned).getEntryInfo(Database.getInstance()).username + .compareTo((object2 as EntryVersioned).getEntryInfo(Database.getInstance()).username, + ignoreCase = true) + } + return NodeTitleComparator(ascending, groupsBefore).compare(object1, object2) + } + } + + /** + * Comparator of node by creation */ class NodeCreationComparator(ascending: Boolean, groupsBefore: Boolean) : NodeComparator(ascending, groupsBefore) { @@ -115,7 +133,7 @@ enum class SortNodeEnum { } /** - * Comparator of node by last modification, Groups first, Entries second + * Comparator of node by last modification */ class NodeLastModificationComparator(ascending: Boolean, groupsBefore: Boolean) : NodeComparator(ascending, groupsBefore) { @@ -126,7 +144,7 @@ enum class SortNodeEnum { } /** - * Comparator of node by last access, Groups first, Entries second + * Comparator of node by last access */ class NodeLastAccessComparator(ascending: Boolean, groupsBefore: Boolean) : NodeComparator(ascending, groupsBefore) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/EntryVersioned.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/EntryVersioned.kt index bd3018318..7eb93df04 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/EntryVersioned.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/EntryVersioned.kt @@ -338,11 +338,24 @@ class EntryVersioned : NodeVersioned, PwEntryInterface { return entryInfo } - /* - ------------ - Class methods - ------------ - */ + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as EntryVersioned + + if (pwEntryV3 != other.pwEntryV3) return false + if (pwEntryV4 != other.pwEntryV4) return false + + return true + } + + override fun hashCode(): Int { + var result = pwEntryV3?.hashCode() ?: 0 + result = 31 * result + (pwEntryV4?.hashCode() ?: 0) + return result + } + companion object CREATOR : Parcelable.Creator { override fun createFromParcel(parcel: Parcel): EntryVersioned { diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt b/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt index b760b8384..4493a23d8 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt @@ -130,7 +130,7 @@ object PreferencesUtil { fun getListSort(context: Context): SortNodeEnum { val prefs = PreferenceManager.getDefaultSharedPreferences(context) prefs.getString(context.getString(R.string.sort_node_key), - SortNodeEnum.TITLE.name)?.let { + SortNodeEnum.DB.name)?.let { return SortNodeEnum.valueOf(it) } return SortNodeEnum.DB diff --git a/app/src/main/res/layout/fragment_sort_selection.xml b/app/src/main/res/layout/fragment_sort_selection.xml index 6dd0fe837..4111c5c82 100644 --- a/app/src/main/res/layout/fragment_sort_selection.xml +++ b/app/src/main/res/layout/fragment_sort_selection.xml @@ -23,8 +23,7 @@ + android:text="@string/sort_username"/> Date: Sat, 17 Aug 2019 16:36:45 +0200 Subject: [PATCH 17/43] Upgrade CHANGELOG --- CHANGELOG | 1 + fastlane/metadata/android/en-US/changelogs/21.txt | 3 ++- fastlane/metadata/android/fr-FR/changelogs/21.txt | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 6f468d440..bfb2588af 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ KeepassDX (2.5.0.0beta20) * Fix nested groups no longer visible in V1 databases * Add natural database sort + * Add username database sort KeepassDX (2.5.0.0beta20) * Fix a major bug that displays an entry history diff --git a/fastlane/metadata/android/en-US/changelogs/21.txt b/fastlane/metadata/android/en-US/changelogs/21.txt index a2dcfe8bc..8f26ad243 100644 --- a/fastlane/metadata/android/en-US/changelogs/21.txt +++ b/fastlane/metadata/android/en-US/changelogs/21.txt @@ -1,2 +1,3 @@ * Fix nested groups no longer visible in V1 databases - * Add natural database sort \ No newline at end of file + * Add natural database sort + * Add username database sort \ No newline at end of file diff --git a/fastlane/metadata/android/fr-FR/changelogs/21.txt b/fastlane/metadata/android/fr-FR/changelogs/21.txt index 48bc984bc..9777ef79a 100644 --- a/fastlane/metadata/android/fr-FR/changelogs/21.txt +++ b/fastlane/metadata/android/fr-FR/changelogs/21.txt @@ -1,2 +1,3 @@ * Correction des groupes imbriqués plus visible dans les bases V1 - * Ajout du tri par ordre naturel de la base \ No newline at end of file + * Ajout du tri par ordre naturel de la base + * Ajout du tri par nom d'utilisateur \ No newline at end of file From 4a28802b02818085a7a862b4c0f4f6fa9c1bfa6c Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sat, 17 Aug 2019 17:11:06 +0200 Subject: [PATCH 18/43] Add RecycleBin at Bottom sort option --- .../keepass/activities/ListNodesFragment.kt | 31 ++++++------- .../kunzisoft/keepass/adapters/NodeAdapter.kt | 12 ++--- .../keepass/database/SortNodeEnum.kt | 44 ++++++++++++------- 3 files changed, 50 insertions(+), 37 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.kt index b234fec63..a30611c41 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.kt @@ -25,6 +25,7 @@ import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.activities.stylish.StylishFragment import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper +import com.kunzisoft.keepass.database.element.Database class ListNodesFragment : StylishFragment(), SortDialogFragment.SortSelectionListener { @@ -205,24 +206,20 @@ class ListNodesFragment : StylishFragment(), SortDialogFragment.SortSelectionLis R.id.menu_sort -> { context?.let { context -> - val sortDialogFragment: SortDialogFragment - - /* - // TODO Recycle bin bottom - if (database.isRecycleBinAvailable() && database.isRecycleBinEnabled()) { - sortDialogFragment = + val sortDialogFragment: SortDialogFragment = + if (Database.getInstance().isRecycleBinAvailable + && Database.getInstance().isRecycleBinEnabled) { SortDialogFragment.getInstance( - PrefsUtil.getListSort(this), - PrefsUtil.getAscendingSort(this), - PrefsUtil.getGroupsBeforeSort(this), - PrefsUtil.getRecycleBinBottomSort(this)); - } else { - */ - sortDialogFragment = SortDialogFragment.getInstance( - PreferencesUtil.getListSort(context), - PreferencesUtil.getAscendingSort(context), - PreferencesUtil.getGroupsBeforeSort(context)) - //} + PreferencesUtil.getListSort(context), + PreferencesUtil.getAscendingSort(context), + PreferencesUtil.getGroupsBeforeSort(context), + PreferencesUtil.getRecycleBinBottomSort(context)) + } else { + SortDialogFragment.getInstance( + PreferencesUtil.getListSort(context), + PreferencesUtil.getAscendingSort(context), + PreferencesUtil.getGroupsBeforeSort(context)) + } sortDialogFragment.show(childFragmentManager, "sortDialog") } diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.kt b/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.kt index 496d52b2c..c9a3a5a2b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.kt +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.kt @@ -48,9 +48,10 @@ class NodeAdapter private var textSize: Float = 0.toFloat() private var subtextSize: Float = 0.toFloat() private var iconSize: Float = 0.toFloat() - private var listSort: SortNodeEnum? = null - private var groupsBeforeSort: Boolean = false - private var ascendingSort: Boolean = false + private var listSort: SortNodeEnum = SortNodeEnum.DB + private var ascendingSort: Boolean = true + private var groupsBeforeSort: Boolean = true + private var recycleBinBottomSort: Boolean = true private var showUserNames: Boolean = false private var nodeClickCallback: NodeClickCallback? = null @@ -79,7 +80,7 @@ class NodeAdapter this.nodeSortedList = SortedList(NodeVersioned::class.java, object : SortedListAdapterCallback(this) { override fun compare(item1: NodeVersioned, item2: NodeVersioned): Int { - return listSort?.getNodeComparator(ascendingSort, groupsBeforeSort)?.compare(item1, item2) ?: 0 + return listSort.getNodeComparator(ascendingSort, groupsBeforeSort, recycleBinBottomSort).compare(item1, item2) } override fun areContentsTheSame(oldItem: NodeVersioned, newItem: NodeVersioned): Boolean { @@ -124,8 +125,9 @@ class NodeAdapter val iconDefaultSize = context.resources.getDimension(R.dimen.list_icon_size_default) this.iconSize = iconDefaultSize * textSize / textSizeDefault this.listSort = PreferencesUtil.getListSort(context) - this.groupsBeforeSort = PreferencesUtil.getGroupsBeforeSort(context) this.ascendingSort = PreferencesUtil.getAscendingSort(context) + this.groupsBeforeSort = PreferencesUtil.getGroupsBeforeSort(context) + this.recycleBinBottomSort = PreferencesUtil.getRecycleBinBottomSort(context) this.showUserNames = PreferencesUtil.showUsernamesListEntries(context) } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.kt b/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.kt index c15c20614..c0a293b60 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.kt @@ -29,18 +29,18 @@ import java.util.* enum class SortNodeEnum { DB, TITLE, USERNAME, CREATION_TIME, LAST_MODIFY_TIME, LAST_ACCESS_TIME; - fun getNodeComparator(ascending: Boolean, groupsBefore: Boolean): Comparator { + fun getNodeComparator(ascending: Boolean, groupsBefore: Boolean, recycleBinBottom: Boolean): Comparator { return when (this) { - DB -> NodeNaturalComparator(ascending, groupsBefore) - TITLE -> NodeTitleComparator(ascending, groupsBefore) - USERNAME -> NodeUsernameComparator(ascending, groupsBefore) - CREATION_TIME -> NodeCreationComparator(ascending, groupsBefore) - LAST_MODIFY_TIME -> NodeLastModificationComparator(ascending, groupsBefore) - LAST_ACCESS_TIME -> NodeLastAccessComparator(ascending, groupsBefore) + DB -> NodeNaturalComparator(ascending, groupsBefore, recycleBinBottom) + TITLE -> NodeTitleComparator(ascending, groupsBefore, recycleBinBottom) + USERNAME -> NodeUsernameComparator(ascending, groupsBefore, recycleBinBottom) + CREATION_TIME -> NodeCreationComparator(ascending, groupsBefore, recycleBinBottom) + LAST_MODIFY_TIME -> NodeLastModificationComparator(ascending, groupsBefore, recycleBinBottom) + LAST_ACCESS_TIME -> NodeLastAccessComparator(ascending, groupsBefore, recycleBinBottom) } } - abstract class NodeComparator(var ascending: Boolean, var groupsBefore: Boolean) : Comparator { + abstract class NodeComparator(var ascending: Boolean, var groupsBefore: Boolean, var recycleBinBottom: Boolean) : Comparator { abstract fun compareBySpecificOrder(object1: NodeVersioned, object2: NodeVersioned): Int @@ -58,6 +58,14 @@ enum class SortNodeEnum { if (object1.type == Type.GROUP) { return if (object2.type == Type.GROUP) { + // RecycleBin at end of groups + if (recycleBinBottom) { + if (Database.getInstance().recycleBin == object1) + return 1 + if (Database.getInstance().recycleBin == object2) + return -1 + } + specificOrderOrHashIfEquals(object1, object2) } else if (object2.type == Type.ENTRY) { if (groupsBefore) @@ -88,7 +96,8 @@ enum class SortNodeEnum { /** * Comparator of node by natural database placement */ - class NodeNaturalComparator(ascending: Boolean, groupsBefore: Boolean) : NodeComparator(ascending, groupsBefore) { + class NodeNaturalComparator(ascending: Boolean, groupsBefore: Boolean, recycleBinBottom: Boolean) + : NodeComparator(ascending, groupsBefore, recycleBinBottom) { override fun compareBySpecificOrder(object1: NodeVersioned, object2: NodeVersioned): Int { return object1.nodePositionInParent.compareTo(object2.nodePositionInParent) @@ -98,7 +107,8 @@ enum class SortNodeEnum { /** * Comparator of Node by Title */ - class NodeTitleComparator(ascending: Boolean, groupsBefore: Boolean) : NodeComparator(ascending, groupsBefore) { + class NodeTitleComparator(ascending: Boolean, groupsBefore: Boolean, recycleBinBottom: Boolean) + : NodeComparator(ascending, groupsBefore, recycleBinBottom) { override fun compareBySpecificOrder(object1: NodeVersioned, object2: NodeVersioned): Int { return object1.title.compareTo(object2.title, ignoreCase = true) @@ -108,7 +118,8 @@ enum class SortNodeEnum { /** * Comparator of Node by Username, Groups by title */ - class NodeUsernameComparator(ascending: Boolean, groupsBefore: Boolean) : NodeComparator(ascending, groupsBefore) { + class NodeUsernameComparator(ascending: Boolean, groupsBefore: Boolean, recycleBinBottom: Boolean) + : NodeComparator(ascending, groupsBefore, recycleBinBottom) { override fun compareBySpecificOrder(object1: NodeVersioned, object2: NodeVersioned): Int { if (object1.type == Type.ENTRY && object2.type == Type.ENTRY) { @@ -117,14 +128,15 @@ enum class SortNodeEnum { .compareTo((object2 as EntryVersioned).getEntryInfo(Database.getInstance()).username, ignoreCase = true) } - return NodeTitleComparator(ascending, groupsBefore).compare(object1, object2) + return NodeTitleComparator(ascending, groupsBefore, recycleBinBottom).compare(object1, object2) } } /** * Comparator of node by creation */ - class NodeCreationComparator(ascending: Boolean, groupsBefore: Boolean) : NodeComparator(ascending, groupsBefore) { + class NodeCreationComparator(ascending: Boolean, groupsBefore: Boolean, recycleBinBottom: Boolean) + : NodeComparator(ascending, groupsBefore, recycleBinBottom) { override fun compareBySpecificOrder(object1: NodeVersioned, object2: NodeVersioned): Int { return object1.creationTime.date @@ -135,7 +147,8 @@ enum class SortNodeEnum { /** * Comparator of node by last modification */ - class NodeLastModificationComparator(ascending: Boolean, groupsBefore: Boolean) : NodeComparator(ascending, groupsBefore) { + class NodeLastModificationComparator(ascending: Boolean, groupsBefore: Boolean, recycleBinBottom: Boolean) + : NodeComparator(ascending, groupsBefore, recycleBinBottom) { override fun compareBySpecificOrder(object1: NodeVersioned, object2: NodeVersioned): Int { return object1.lastModificationTime.date @@ -146,7 +159,8 @@ enum class SortNodeEnum { /** * Comparator of node by last access */ - class NodeLastAccessComparator(ascending: Boolean, groupsBefore: Boolean) : NodeComparator(ascending, groupsBefore) { + class NodeLastAccessComparator(ascending: Boolean, groupsBefore: Boolean, recycleBinBottom: Boolean) + : NodeComparator(ascending, groupsBefore, recycleBinBottom) { override fun compareBySpecificOrder(object1: NodeVersioned, object2: NodeVersioned): Int { return object1.lastAccessTime.date From 03ab688abedf4c28ee3d9e555dea97f969fda1c1 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sun, 18 Aug 2019 11:10:07 +0200 Subject: [PATCH 19/43] Fix bug open button disabled with only keyFile #295 --- .../keepass/activities/PasswordActivity.kt | 31 ++++++++++--------- app/src/main/res/values/strings.xml | 4 +-- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt index 14dbe41ac..638e99320 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt @@ -48,7 +48,6 @@ import com.kunzisoft.keepass.activities.dialogs.PasswordEncodingDialogFragment import com.kunzisoft.keepass.activities.helpers.* import com.kunzisoft.keepass.activities.lock.LockingActivity import com.kunzisoft.keepass.activities.stylish.StylishActivity -import com.kunzisoft.keepass.app.App import com.kunzisoft.keepass.autofill.AutofillHelper import com.kunzisoft.keepass.database.action.LoadDatabaseRunnable import com.kunzisoft.keepass.database.action.ProgressDialogThread @@ -148,10 +147,8 @@ class PasswordActivity : StylishActivity(), } }) - enableButtonOnCheckedChangeListener = CompoundButton.OnCheckedChangeListener { _, isChecked -> - if (!PreferencesUtil.emptyPasswordAllowed(this@PasswordActivity)) { - confirmButtonView?.isEnabled = isChecked - } + enableButtonOnCheckedChangeListener = CompoundButton.OnCheckedChangeListener { _, _ -> + enableOrNotTheConfirmationButton() } } @@ -175,15 +172,6 @@ class PasswordActivity : StylishActivity(), // For check shutdown super.onResume() - // Enable or not the open button - if (!PreferencesUtil.emptyPasswordAllowed(this@PasswordActivity)) { - checkboxPasswordView?.let { - confirmButtonView?.isEnabled = it.isChecked - } - } else { - confirmButtonView?.isEnabled = true - } - UriIntentInitTask(WeakReference(this), this, mRememberKeyFile) .execute(intent) } @@ -288,6 +276,21 @@ class PasswordActivity : StylishActivity(), if (!fingerPrintInit) { checkboxPasswordView?.setOnCheckedChangeListener(enableButtonOnCheckedChangeListener) } + checkboxKeyFileView?.setOnCheckedChangeListener(enableButtonOnCheckedChangeListener) + } + + enableOrNotTheConfirmationButton() + } + + private fun enableOrNotTheConfirmationButton() { + // Enable or not the open button if setting is checked + if (!PreferencesUtil.emptyPasswordAllowed(this@PasswordActivity)) { + checkboxPasswordView?.let { + confirmButtonView?.isEnabled = (checkboxPasswordView?.isChecked == true + || checkboxKeyFileView?.isChecked == true) + } + } else { + confirmButtonView?.isEnabled = true } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 09103a1c0..692f41a03 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -329,8 +329,8 @@ Sound on keypress - Allow no password - Enable the \"Open\" button if no password identification is selected + Allow no master key + Enable the \"Open\" button if no credentials are selected Write-protected Open your database read-only by default Delete password From a0c07654dfb9b349769a8c9a7a07936eb61afe35 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sun, 18 Aug 2019 11:14:07 +0200 Subject: [PATCH 20/43] Update CHANGELOGS --- CHANGELOG | 2 ++ fastlane/metadata/android/en-US/changelogs/21.txt | 4 +++- fastlane/metadata/android/fr-FR/changelogs/21.txt | 4 +++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index bfb2588af..24b730d0e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,7 +1,9 @@ KeepassDX (2.5.0.0beta20) * Fix nested groups no longer visible in V1 databases + * Improved data import algorithm for V1 databases * Add natural database sort * Add username database sort + * Fix button disabled with only KeyFile KeepassDX (2.5.0.0beta20) * Fix a major bug that displays an entry history diff --git a/fastlane/metadata/android/en-US/changelogs/21.txt b/fastlane/metadata/android/en-US/changelogs/21.txt index 8f26ad243..5ed60699b 100644 --- a/fastlane/metadata/android/en-US/changelogs/21.txt +++ b/fastlane/metadata/android/en-US/changelogs/21.txt @@ -1,3 +1,5 @@ * Fix nested groups no longer visible in V1 databases + * Improved data import algorithm for V1 databases * Add natural database sort - * Add username database sort \ No newline at end of file + * Add username database sort + * Fix button disabled with only KeyFile \ No newline at end of file diff --git a/fastlane/metadata/android/fr-FR/changelogs/21.txt b/fastlane/metadata/android/fr-FR/changelogs/21.txt index 9777ef79a..c420ac3bd 100644 --- a/fastlane/metadata/android/fr-FR/changelogs/21.txt +++ b/fastlane/metadata/android/fr-FR/changelogs/21.txt @@ -1,3 +1,5 @@ * Correction des groupes imbriqués plus visible dans les bases V1 + * Amélioration de l'algortihme d'import des données pour les bases V1 * Ajout du tri par ordre naturel de la base - * Ajout du tri par nom d'utilisateur \ No newline at end of file + * Ajout du tri par nom d'utilisateur + * Correction du bouton désactivé avec seulement un fichier de clé \ No newline at end of file From f6d6c134f4eef470260194e221db13d18b14dc66 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sun, 18 Aug 2019 14:12:31 +0200 Subject: [PATCH 21/43] Fix colorTextInverse --- app/src/main/res/values/colors.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index af8e85b67..1beb38ec0 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -62,7 +62,7 @@ #cccccc #616161 #c7c7c7 - #eeeeee + #FFFFFF #565656 #7c7c7c #c7c7c7 From f605d36adf5a057e67d6c14985dcefd84d62e9b5 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sun, 18 Aug 2019 14:17:04 +0200 Subject: [PATCH 22/43] Remove TODO sort --- .../main/java/com/kunzisoft/keepass/adapters/NodeAdapter.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.kt b/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.kt index c9a3a5a2b..6a8873071 100644 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.kt +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.kt @@ -137,14 +137,12 @@ class NodeAdapter fun rebuildList(group: GroupVersioned) { this.nodeSortedList.clear() assignPreferences() - // TODO verify sort try { this.nodeSortedList.addAll(group.getChildren()) } catch (e: Exception) { Log.e(TAG, "Can't add node elements to the list", e) Toast.makeText(context, "Can't add node elements to the list : " + e.message, Toast.LENGTH_LONG).show() } - } /** @@ -240,6 +238,9 @@ class NodeAdapter holder.icon.layoutParams?.width = iconSize.toInt() holder.text.textSize = textSize holder.subText.textSize = subtextSize + if (subNode.type == Type.GROUP) { + holder.numberChildren?.text = (subNode as GroupVersioned).getChildEntries().size.toString() + } } override fun getItemCount(): Int { @@ -357,6 +358,7 @@ class NodeAdapter var icon: ImageView = itemView.findViewById(R.id.node_icon) var text: TextView = itemView.findViewById(R.id.node_text) var subText: TextView = itemView.findViewById(R.id.node_subtext) + var numberChildren: TextView? = itemView.findViewById(R.id.node_child_numbers) } companion object { From 639c6dc4acaef76d4cba5fb9d41c1f2fd6244e6b Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sun, 18 Aug 2019 14:43:05 +0200 Subject: [PATCH 23/43] Add number of entries in node view --- .../com/kunzisoft/keepass/adapters/NodeAdapter.kt | 13 +++++++++++-- .../kunzisoft/keepass/settings/PreferencesUtil.kt | 6 ++++++ app/src/main/res/drawable/background_text_info.xml | 14 ++++++++++++++ app/src/main/res/layout/item_list_nodes_entry.xml | 3 +++ app/src/main/res/layout/item_list_nodes_group.xml | 14 ++++++++++++++ app/src/main/res/values/donottranslate.xml | 2 ++ app/src/main/res/values/strings.xml | 2 ++ app/src/main/res/values/styles.xml | 7 +++++++ app/src/main/res/xml/appearance_preferences.xml | 5 +++++ 9 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 app/src/main/res/drawable/background_text_info.xml diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.kt b/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.kt index 6a8873071..799505e59 100644 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.kt +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.kt @@ -52,7 +52,8 @@ class NodeAdapter private var ascendingSort: Boolean = true private var groupsBeforeSort: Boolean = true private var recycleBinBottomSort: Boolean = true - private var showUserNames: Boolean = false + private var showUserNames: Boolean = true + private var showNumberEntries: Boolean = true private var nodeClickCallback: NodeClickCallback? = null private var nodeMenuListener: NodeMenuListener? = null @@ -129,6 +130,7 @@ class NodeAdapter this.groupsBeforeSort = PreferencesUtil.getGroupsBeforeSort(context) this.recycleBinBottomSort = PreferencesUtil.getRecycleBinBottomSort(context) this.showUserNames = PreferencesUtil.showUsernamesListEntries(context) + this.showNumberEntries = PreferencesUtil.showNumberEntries(context) } /** @@ -239,7 +241,14 @@ class NodeAdapter holder.text.textSize = textSize holder.subText.textSize = subtextSize if (subNode.type == Type.GROUP) { - holder.numberChildren?.text = (subNode as GroupVersioned).getChildEntries().size.toString() + if (showNumberEntries) { + holder.numberChildren?.apply { + text = (subNode as GroupVersioned).getChildEntries().size.toString() + visibility = View.VISIBLE + } + } else { + holder.numberChildren?.visibility = View.GONE + } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt b/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt index 4493a23d8..d07d01aa0 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt @@ -40,6 +40,12 @@ object PreferencesUtil { context.resources.getBoolean(R.bool.list_entries_show_username_default)) } + fun showNumberEntries(context: Context): Boolean { + val prefs = PreferenceManager.getDefaultSharedPreferences(context) + return prefs.getBoolean(context.getString(R.string.list_groups_show_number_entries_key), + context.resources.getBoolean(R.bool.list_groups_show_number_entries_default)) + } + /** * Retrieve the text size in SP, verify the integrity of the size stored in preference */ diff --git a/app/src/main/res/drawable/background_text_info.xml b/app/src/main/res/drawable/background_text_info.xml new file mode 100644 index 000000000..981df6281 --- /dev/null +++ b/app/src/main/res/drawable/background_text_info.xml @@ -0,0 +1,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_list_nodes_entry.xml b/app/src/main/res/layout/item_list_nodes_entry.xml index 58f8f9d8b..68e237a15 100644 --- a/app/src/main/res/layout/item_list_nodes_entry.xml +++ b/app/src/main/res/layout/item_list_nodes_entry.xml @@ -20,6 +20,7 @@ @@ -61,6 +62,7 @@ android:id="@+id/node_text" android:layout_height="wrap_content" android:layout_width="wrap_content" + tools:text="Node Title" android:lines="1" android:singleLine="true" style="@style/KeepassDXStyle.TextAppearance.Default" /> @@ -69,6 +71,7 @@ android:layout_height="wrap_content" android:layout_width="wrap_content" android:layout_marginTop="-4dp" + tools:text="Node SubTitle" android:lines="1" android:singleLine="true" style="@style/KeepassDXStyle.TextAppearance.Secondary" /> diff --git a/app/src/main/res/layout/item_list_nodes_group.xml b/app/src/main/res/layout/item_list_nodes_group.xml index 5eba1c3ff..06095afb0 100644 --- a/app/src/main/res/layout/item_list_nodes_group.xml +++ b/app/src/main/res/layout/item_list_nodes_group.xml @@ -20,6 +20,7 @@ @@ -57,6 +58,17 @@ android:layout_centerVertical="true" android:layout_toRightOf="@+id/group_arrow" android:layout_toEndOf="@+id/group_arrow" /> + @@ -81,6 +94,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center_vertical" + tools:text="Node SubTitle" android:layout_marginTop="-4dp" android:lines="1" android:singleLine="true" diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml index a6044a72b..e072431ef 100644 --- a/app/src/main/res/values/donottranslate.xml +++ b/app/src/main/res/values/donottranslate.xml @@ -132,6 +132,8 @@ setting_icon_pack_choose_key list_entries_show_username_key true + list_groups_show_number_entries_key + true list_size monospace_font_extra_fields_enable_key true diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 692f41a03..388c78039 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -113,6 +113,8 @@ Length Show usernames Show usernames in entry lists + Show number of entries + Show the number of entries in a group Size of list items Text size in the element list Loading database… diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 31c54f0b1..7f4fcb79b 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -270,6 +270,13 @@ ?attr/colorAccent + + + + +